Merge remote-tracking branch 'parallax/master'

Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
This commit is contained in:
Matt Liberty 2024-07-26 20:42:05 -07:00
commit 9a490b1b13
142 changed files with 7892 additions and 8427 deletions

View File

@ -24,13 +24,12 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14)
cmake_policy(SET CMP0086 NEW) cmake_policy(SET CMP0086 NEW)
endif() endif()
project(STA VERSION 2.5.0 project(STA VERSION 2.6.0
LANGUAGES CXX LANGUAGES CXX
) )
option(USE_CUDD "Use CUDD BDD package")
option(CUDD_DIR "CUDD BDD package directory") 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") option(USE_SANITIZE "Compile with santize address enabled")
# Turn on to debug compiler args. # Turn on to debug compiler args.
@ -69,7 +68,6 @@ set(STA_SOURCE
dcalc/ArnoldiDelayCalc.cc dcalc/ArnoldiDelayCalc.cc
dcalc/ArnoldiReduce.cc dcalc/ArnoldiReduce.cc
dcalc/CcsCeffDelayCalc.cc dcalc/CcsCeffDelayCalc.cc
dcalc/CcsSimDelayCalc.cc
dcalc/DcalcAnalysisPt.cc dcalc/DcalcAnalysisPt.cc
dcalc/DelayCalc.cc dcalc/DelayCalc.cc
dcalc/DelayCalcBase.cc dcalc/DelayCalcBase.cc
@ -195,8 +193,10 @@ set(STA_SOURCE
search/VisitPathEnds.cc search/VisitPathEnds.cc
search/VisitPathGroupVertices.cc search/VisitPathGroupVertices.cc
search/WorstSlack.cc search/WorstSlack.cc
search/WritePathSpice.cc
search/WriteSpice.cc spice/WritePathSpice.cc
spice/WriteSpice.cc
spice/Xyce.cc
power/Power.cc power/Power.cc
power/ReadVcdActivities.cc power/ReadVcdActivities.cc
@ -233,22 +233,22 @@ set(STA_TCL_FILES
tcl/Util.tcl tcl/Util.tcl
tcl/CmdArgs.tcl tcl/CmdArgs.tcl
tcl/CmdUtil.tcl tcl/CmdUtil.tcl
tcl/Graph.tcl
tcl/Liberty.tcl
tcl/Link.tcl
tcl/Network.tcl
tcl/NetworkEdit.tcl
tcl/Property.tcl tcl/Property.tcl
tcl/Sdc.tcl
tcl/Search.tcl
tcl/Sta.tcl tcl/Sta.tcl
tcl/Splash.tcl tcl/Splash.tcl
tcl/Variables.tcl tcl/Variables.tcl
tcl/WritePathSpice.tcl
dcalc/DelayCalc.tcl dcalc/DelayCalc.tcl
graph/Graph.tcl
liberty/Liberty.tcl
network/Link.tcl
network/Network.tcl
network/NetworkEdit.tcl
parasitics/Parasitics.tcl parasitics/Parasitics.tcl
power/Power.tcl power/Power.tcl
sdc/Sdc.tcl
sdf/Sdf.tcl sdf/Sdf.tcl
search/Search.tcl
spice/WriteSpice.tcl
verilog/Verilog.tcl verilog/Verilog.tcl
) )
@ -258,8 +258,7 @@ set(STA_TCL_FILES
# #
################################################################ ################################################################
# Earlier versions of flex use 'register' declarations that are deprecated # Earlier versions of flex use 'register' declarations that are illegal in c++17.
# in c++11 and illegal in c++17.
#find_package(FLEX 2.6.4) #find_package(FLEX 2.6.4)
find_package(FLEX) find_package(FLEX)
find_package(BISON) find_package(BISON)
@ -371,30 +370,25 @@ find_package(Eigen3 REQUIRED)
# #
# Locate CUDD bdd package. # Locate CUDD bdd package.
# #
set(CUDD 0) find_library(CUDD_LIB
if (USE_CUDD) NAME cudd
find_library(CUDD_LIB PATHS ${CUDD_DIR}
NAME cudd PATH_SUFFIXES lib lib/cudd cudd/.libs
PATHS ${CUDD_DIR} )
PATH_SUFFIXES lib lib/cudd cudd/.libs if (CUDD_LIB)
) message(STATUS "CUDD library: ${CUDD_LIB}")
if (CUDD_LIB) get_filename_component(CUDD_LIB_DIR "${CUDD_LIB}" PATH)
message(STATUS "CUDD library: ${CUDD_LIB}") get_filename_component(CUDD_LIB_PARENT1 "${CUDD_LIB_DIR}" PATH)
get_filename_component(CUDD_LIB_DIR "${CUDD_LIB}" PATH) find_file(CUDD_HEADER cudd.h
get_filename_component(CUDD_LIB_PARENT1 "${CUDD_LIB_DIR}" PATH) PATHS ${CUDD_LIB_PARENT1} ${CUDD_LIB_PARENT1}/include ${CUDD_LIB_PARENT1}/include/cudd)
find_file(CUDD_HEADER cudd.h if (CUDD_HEADER)
PATHS ${CUDD_LIB_PARENT1} ${CUDD_LIB_PARENT1}/include ${CUDD_LIB_PARENT1}/include/cudd) get_filename_component(CUDD_INCLUDE "${CUDD_HEADER}" PATH)
if (CUDD_HEADER) message(STATUS "CUDD header: ${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()
else() else()
message(STATUS "CUDD library: not found") message(STATUS "CUDD header: not found")
endif() endif()
else()
message(STATUS "CUDD library: not found")
endif() endif()
if("${SSTA}" STREQUAL "") if("${SSTA}" STREQUAL "")
@ -426,23 +420,24 @@ set_property(SOURCE ${STA_SWIG_FILE}
PROPERTY SWIG_FLAGS PROPERTY SWIG_FLAGS
-module sta -module sta
-namespace -prefix sta -namespace -prefix sta
-I${STA_HOME}/tcl -I${STA_HOME}
-I${STA_HOME}/sdf
-I${STA_HOME}/dcalc
-I${STA_HOME}/parasitics
-I${STA_HOME}/power
-I${STA_HOME}/verilog
) )
set(SWIG_FILES set(SWIG_FILES
${STA_HOME}/dcalc/DelayCalc.i ${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}/parasitics/Parasitics.i
${STA_HOME}/power/Power.i ${STA_HOME}/power/Power.i
${STA_HOME}/sdc/Sdc.i
${STA_HOME}/sdf/Sdf.i ${STA_HOME}/sdf/Sdf.i
${STA_HOME}/search/Search.i
${STA_HOME}/spice/WriteSpice.i
${STA_HOME}/tcl/Exception.i ${STA_HOME}/tcl/Exception.i
${STA_HOME}/tcl/StaTcl.i
${STA_HOME}/tcl/StaTclTypes.i ${STA_HOME}/tcl/StaTclTypes.i
${STA_HOME}/tcl/NetworkEdit.i ${STA_HOME}/util/Util.i
${STA_HOME}/verilog/Verilog.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 # No simple way to modify the swig template that emits code full of warnings
# so suppress them. # so suppress them.
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-missing-braces;-Wno-deprecated-declarations" COMPILE_OPTIONS "-Wno-cast-qual;-Wno-missing-braces;-Wno-deprecated-declarations"
INCLUDE_DIRECTORIES "${CUDD_INCLUDE}"
) )
target_link_libraries(sta_swig target_link_libraries(sta_swig
@ -515,6 +509,7 @@ target_link_libraries(OpenSTA
Eigen3::Eigen Eigen3::Eigen
${TCL_LIBRARY} ${TCL_LIBRARY}
${CMAKE_THREAD_LIBS_INIT} ${CMAKE_THREAD_LIBS_INIT}
${CUDD_LIB}
) )
if (TCL_READLINE_LIBRARY) if (TCL_READLINE_LIBRARY)
@ -525,9 +520,7 @@ if (ZLIB_LIBRARIES)
target_link_libraries(OpenSTA ${ZLIB_LIBRARIES}) target_link_libraries(OpenSTA ${ZLIB_LIBRARIES})
endif() endif()
if (CUDD_LIB) target_link_libraries(OpenSTA )
target_link_libraries(OpenSTA ${CUDD_LIB})
endif()
target_include_directories(OpenSTA target_include_directories(OpenSTA
PUBLIC PUBLIC
@ -559,7 +552,7 @@ target_compile_options(OpenSTA
# Disable compiler specific extensions like gnu++11. # Disable compiler specific extensions like gnu++11.
set_target_properties(OpenSTA PROPERTIES CXX_EXTENSIONS OFF) 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 # Executable

View File

@ -85,15 +85,15 @@ The build dependency versions are show below. Other versions may
work, but these are the versions used for development. work, but these are the versions used for development.
``` ```
from Ubuntu Macos Ubuntu Macos
22.04.2 14.4.1 22.04.2 14.5
cmake 3.10.2 3.24.2 3.29.2 cmake 3.24.2 3.29.2
clang 9.1.0 15.0.0 clang 15.0.0
gcc 3.3.2 11.4.0 gcc 11.4.0
tcl 8.4 8.6 8.6.6 tcl 8.6 8.6.6
swig 1.3.28 4.1.0 4.2.1 swig 4.1.0 4.1.1
bison 1.35 3.8.2 3.8.2 bison 3.8.2 3.8.2
flex 2.5.4 2.6.4 2.6.4 flex 2.6.4 2.6.4
``` ```
Note that flex versions before 2.6.4 contain 'register' declarations that Note that flex versions before 2.6.4 contain 'register' declarations that
@ -101,38 +101,47 @@ are illegal in c++17.
External library dependencies: External library dependencies:
``` ```
from Ubuntu Macos Ubuntu Macos license
eigen 3.4 .0 3.4.0 required eigen 3.4.0 3.4.0 MPL2 required
tclreadline 2.3.8 optional cudd 3.0.0 3.0.0 BSD required
libz 1.1.4 1.2.5 1.2.8 optional tclreadline 2.3.8 2.3.8 BSD optional
cudd 2.4.1 3.0.0 optional zLib 1.2.5 1.2.8 zlib optional
``` ```
The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html) The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html)
links the GNU readline library to the TCL interpreter for command line editing 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 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 (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 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 CUDD is a binary decision diageram (BDD) package that is used to
improve conditional timing arc handling. OpenSTA does not require it 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) [here](https://www.davidkebo.com/source/cudd_versions/cudd-3.0.0.tar.gz)
or [here](https://sourceforge.net/projects/cudd-mirror/). or [here](https://sourceforge.net/projects/cudd-mirror/).
Note that the file hierarchy of the CUDD installation changed with version 3.0. Use the CUDD_DIR option to set the install directory of the CUDD
Some changes to CMakeLists.txt are required to support older versions. 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 cmake -DCUDD_DIR=$HOME/stax/cudd-3.0.0 .."
one of the normal install directories. ```
When building CUDD you may use the `--prefix ` option to `configure` to When building CUDD you may use the `--prefix ` option to `configure` to
install in a location other than the default (`/usr/local/lib`). install in a location other than the default (`/usr/local/lib`).

View File

@ -19,6 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <cstdlib> // exit #include <cstdlib> // exit
#include <filesystem>
#include <tcl.h> #include <tcl.h>
#if TCL_READLINE #if TCL_READLINE
#include <tclreadline.h> #include <tclreadline.h>
@ -39,7 +40,6 @@ using sta::evalTclInit;
using sta::sourceTclFile; using sta::sourceTclFile;
using sta::parseThreadsArg; using sta::parseThreadsArg;
using sta::tcl_inits; using sta::tcl_inits;
using sta::is_regular_file;
// Swig uses C linkage for init functions. // Swig uses C linkage for init functions.
extern "C" { extern "C" {
@ -133,15 +133,15 @@ staTclAppInit(int argc,
string init_path = home; string init_path = home;
init_path += "/"; init_path += "/";
init_path += init_filename; 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); sourceTclFile(init_path.c_str(), true, true, interp);
} }
} }
bool exit_after_cmd_file = findCmdLineFlag(argc, argv, "-exit"); bool exit_after_cmd_file = findCmdLineFlag(argc, argv, "-exit");
if (argc > 2 || if (argc > 2
(argc > 1 && argv[1][0] == '-')) { || (argc > 1 && argv[1][0] == '-')) {
showUsage(argv[0], init_filename); showUsage(argv[0], init_filename);
exit(1); exit(1);
} }

View File

@ -16,12 +16,18 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
%include "Exception.i" %include "tcl/Exception.i"
%include "StaTclTypes.i" %include "tcl/StaTclTypes.i"
%include "StaTcl.i" %include "dcalc/DelayCalc.i"
%include "Verilog.i" %include "graph/Graph.i"
%include "NetworkEdit.i" %include "liberty/Liberty.i"
%include "Sdf.i" %include "network/Network.i"
%include "DelayCalc.i" %include "network/NetworkEdit.i"
%include "Parasitics.i" %include "parasitics/Parasitics.i"
%include "Power.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"

View File

@ -51,10 +51,11 @@ findCmdLineFlag(int &argc,
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
char *arg = argv[i]; char *arg = argv[i];
if (stringEq(arg, flag)) { if (stringEq(arg, flag)) {
// remove flag from argv. // Remove flag from argv.
for (int j = i + 1; j < argc; j++, i++) for (int j = i + 1; j < argc; j++, i++)
argv[i] = argv[j]; argv[i] = argv[j];
argc--; argc--;
argv[argc] = nullptr;
return true; return true;
} }
} }
@ -70,10 +71,11 @@ findCmdLineKey(int &argc,
char *arg = argv[i]; char *arg = argv[i];
if (stringEq(arg, key) && i + 1 < argc) { if (stringEq(arg, key) && i + 1 < argc) {
char *value = argv[i + 1]; 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++) for (int j = i + 2; j < argc; j++, i++)
argv[i] = argv[j]; argv[i] = argv[j];
argc -= 2; argc -= 2;
argv[argc] = nullptr;
return value; return value;
} }
} }

View File

@ -14,30 +14,64 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <memory>
#include "ArcDcalcWaveforms.hh" #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 { namespace sta {
Table1 using std::make_shared;
ArcDcalcWaveforms::inputWaveform(const Pin *,
const RiseFall *,
const Corner *,
const MinMax *)
{
return Table1();
}
Table1 Waveform
ArcDcalcWaveforms::drvrRampWaveform(const Pin *, ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg,
const RiseFall *, const DcalcAnalysisPt *dcalc_ap,
const Pin *, const StaState *sta)
const RiseFall *,
const Pin *,
const Corner *,
const MinMax *)
{ {
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<TableAxis>(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 } // namespace

View File

@ -22,37 +22,25 @@
namespace sta { namespace sta {
class StaState;
class Corner; class Corner;
class DcalcAnalysisPt; class DcalcAnalysisPt;
class ArcDcalcArg;
// Abstract class for the graph delay calculator traversal to interface // Abstract class for delay calculation waveforms for ploting.
class ArcDcalcWaveforms class ArcDcalcWaveforms
{ {
public: public:
virtual Table1 inputWaveform(const Pin *in_pin, // Record waveform for drvr/load pin.
const RiseFall *in_rf, virtual void watchPin(const Pin *pin) = 0;
const Corner *corner, virtual void clearWatchPins() = 0;
const MinMax *min_max); virtual PinSeq watchPins() const = 0;
virtual Table1 drvrWaveform(const Pin *in_pin, virtual Waveform watchWaveform(const Pin *pin) = 0;
const RiseFall *in_rf,
const Pin *drvr_pin, protected:
const RiseFall *drvr_rf, Waveform inputWaveform(ArcDcalcArg &dcalc_arg,
const Corner *corner, const DcalcAnalysisPt *dcalc_ap,
const MinMax *min_max) = 0; const StaState *sta);
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);
}; };
} // namespace } // namespace

View File

@ -16,9 +16,11 @@
#include "ArcDelayCalc.hh" #include "ArcDelayCalc.hh"
#include "Units.hh"
#include "Liberty.hh" #include "Liberty.hh"
#include "TimingArc.hh" #include "TimingArc.hh"
#include "Network.hh" #include "Network.hh"
#include "Graph.hh"
namespace sta { 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() : ArcDcalcArg::ArcDcalcArg() :
in_pin_(nullptr), in_pin_(nullptr),
drvr_pin_(nullptr), drvr_pin_(nullptr),
edge_(nullptr), edge_(nullptr),
arc_(nullptr), arc_(nullptr),
in_slew_(0.0), in_slew_(0.0),
load_cap_(0.0),
parasitic_(nullptr), parasitic_(nullptr),
input_delay_(0.0) input_delay_(0.0)
{ {
@ -64,12 +121,14 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
Edge *edge, Edge *edge,
const TimingArc *arc, const TimingArc *arc,
const Slew in_slew, const Slew in_slew,
float load_cap,
const Parasitic *parasitic) : const Parasitic *parasitic) :
in_pin_(in_pin), in_pin_(in_pin),
drvr_pin_(drvr_pin), drvr_pin_(drvr_pin),
edge_(edge), edge_(edge),
arc_(arc), arc_(arc),
in_slew_(in_slew), in_slew_(in_slew),
load_cap_(load_cap),
parasitic_(parasitic), parasitic_(parasitic),
input_delay_(0.0) input_delay_(0.0)
{ {
@ -85,6 +144,7 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
edge_(edge), edge_(edge),
arc_(arc), arc_(arc),
in_slew_(0.0), in_slew_(0.0),
load_cap_(0.0),
parasitic_(nullptr), parasitic_(nullptr),
input_delay_(input_delay) input_delay_(input_delay)
{ {
@ -96,6 +156,7 @@ ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) :
edge_(arg.edge_), edge_(arg.edge_),
arc_(arg.arc_), arc_(arg.arc_),
in_slew_(arg.in_slew_), in_slew_(arg.in_slew_),
load_cap_(arg.load_cap_),
parasitic_(arg.parasitic_), parasitic_(arg.parasitic_),
input_delay_(arg.input_delay_) input_delay_(arg.input_delay_)
{ {
@ -107,6 +168,12 @@ ArcDcalcArg::inEdge() const
return arc_->fromEdge()->asRiseFall(); return arc_->fromEdge()->asRiseFall();
} }
Vertex *
ArcDcalcArg::drvrVertex(const Graph *graph) const
{
return edge_->to(graph);
}
LibertyCell * LibertyCell *
ArcDcalcArg::drvrCell() const ArcDcalcArg::drvrCell() const
{ {
@ -150,6 +217,18 @@ ArcDcalcArg::setParasitic(const Parasitic *parasitic)
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() : ArcDcalcResult::ArcDcalcResult() :

View File

@ -115,6 +115,7 @@ public:
ArnoldiDelayCalc(StaState *sta); ArnoldiDelayCalc(StaState *sta);
virtual ~ArnoldiDelayCalc(); virtual ~ArnoldiDelayCalc();
ArcDelayCalc *copy() override; ArcDelayCalc *copy() override;
const char *name() const override { return "arnoldi"; }
Parasitic *findParasitic(const Pin *drvr_pin, Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
@ -382,7 +383,7 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin,
ConcreteParasitic *cparasitic = ConcreteParasitic *cparasitic =
reinterpret_cast<ConcreteParasitic*>(const_cast<Parasitic*>(parasitic)); reinterpret_cast<ConcreteParasitic*>(const_cast<Parasitic*>(parasitic));
rcmodel_ = dynamic_cast<rcmodel*>(cparasitic); rcmodel_ = dynamic_cast<rcmodel*>(cparasitic);
GateTableModel *table_model = gateTableModel(arc, dcalc_ap); GateTableModel *table_model = arc->gateTableModel(dcalc_ap);
if (table_model && rcmodel_) { if (table_model && rcmodel_) {
const Pvt *pvt = pinPvt(drvr_pin, dcalc_ap); const Pvt *pvt = pinPvt(drvr_pin, dcalc_ap);
return gateDelaySlew(drvr_cell, arc, table_model, in_slew, load_pin_index_map, pvt); return gateDelaySlew(drvr_cell, arc, table_model, in_slew, load_pin_index_map, pvt);

View File

@ -52,6 +52,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
// Includes the Vh:Vdd region. // Includes the Vh:Vdd region.
region_count_(0), region_count_(0),
vl_fail_(false), vl_fail_(false),
watch_pin_values_(network_),
capacitance_unit_(units_->capacitanceUnit()), capacitance_unit_(units_->capacitanceUnit()),
table_dcalc_(makeDmpCeffElmoreDelayCalc(sta)) table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
{ {
@ -82,7 +83,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
parasitic_ = parasitic; parasitic_ = parasitic;
output_waveforms_ = nullptr; output_waveforms_ = nullptr;
GateTableModel *table_model = gateTableModel(arc, dcalc_ap); GateTableModel *table_model = arc->gateTableModel(dcalc_ap);
if (table_model && parasitic) { if (table_model && parasitic) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms(); OutputWaveforms *output_waveforms = table_model->outputWaveforms();
parasitics_->piModel(parasitic, c2_, rpi_, c1_); parasitics_->piModel(parasitic, c2_, rpi_, c1_);
@ -95,25 +96,25 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
bool vdd_exists; bool vdd_exists;
LibertyCell *drvr_cell = arc->to()->libertyCell(); LibertyCell *drvr_cell = arc->to()->libertyCell();
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); 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); drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!vdd_exists) if (!vdd_exists)
report_->error(1700, "VDD not defined in library %s", drvr_library->name()); report_->error(1700, "VDD not defined in library %s", drvr_library->name());
vth_ = drvr_library->outputThreshold(rf) * vdd_; vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(rf) * vdd_; vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
vh_ = drvr_library->slewUpperThreshold(rf) * vdd_; vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
drvr_cell->ensureVoltageWaveforms(); drvr_cell->ensureVoltageWaveforms(dcalc_ap);
in_slew_ = delayAsFloat(in_slew); in_slew_ = delayAsFloat(in_slew);
output_waveforms_ = output_waveforms; output_waveforms_ = output_waveforms;
ref_time_ = output_waveforms_->referenceTime(in_slew_); ref_time_ = output_waveforms_->referenceTime(in_slew_);
debugPrint(debug_, "ccs_dcalc", 1, "%s %s", debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
drvr_cell->name(), drvr_cell->name(),
rf->asString()); drvr_rf_->asString());
ArcDelay gate_delay; ArcDelay gate_delay;
Slew drvr_slew; Slew drvr_slew;
gateDelaySlew(drvr_library, rf, gate_delay, drvr_slew); gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew);
return makeResult(drvr_library, rf, gate_delay, drvr_slew, load_pin_index_map); return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map);
} }
} }
return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, 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.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew); dcalc_result.setDrvrSlew(drvr_slew);
for (auto load_pin_index : load_pin_index_map) { for (const auto [load_pin, load_idx] : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
ArcDelay wire_delay; ArcDelay wire_delay;
Slew load_slew; Slew load_slew;
loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, 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. // Waveform accessors for swig/tcl.
Table1 void
CcsCeffDelayCalc::drvrWaveform(const Pin *in_pin, CcsCeffDelayCalc::watchPin(const Pin *pin)
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max)
{ {
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin, watch_pin_values_[pin] = FloatSeq();
drvr_rf, corner, min_max); }
if (dcalc_success)
return drvrWaveform(in_slew_, drvr_rf); 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<TableAxis>(TableAxisVariable::time,
drvr_times);
Table1 drvr_table(drvr_volts, drvr_time_axis);
return drvr_table;
}
else else
return Table1(); return Table1();
} }
Table1 Waveform
CcsCeffDelayCalc::drvrWaveform(const Slew &in_slew, CcsCeffDelayCalc::loadWaveform(const Pin *load_pin)
const RiseFall *drvr_rf)
{ {
// Stitch together the ccs waveforms for each region. if (output_waveforms_) {
FloatSeq *drvr_times = new FloatSeq; bool elmore_exists = false;
FloatSeq *drvr_volts = new FloatSeq; float elmore = 0.0;
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<TableAxis>(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_) {
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin, if (elmore_exists) {
drvr_rf, corner, min_max);
if (dcalc_success
&& elmore_exists) {
FloatSeq *load_times = new FloatSeq; FloatSeq *load_times = new FloatSeq;
FloatSeq *load_volts = new FloatSeq; FloatSeq *load_volts = new FloatSeq;
double t_vh = findVlTime(vh_, elmore); double t_vh = findVlTime(vh_, elmore);
@ -540,7 +545,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *in_pin,
double ignore; double ignore;
vl(t, elmore, v, 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); load_volts->push_back(v1);
} }
TableAxisPtr load_time_axis = make_shared<TableAxis>(TableAxisVariable::time, TableAxisPtr load_time_axis = make_shared<TableAxis>(TableAxisVariable::time,
@ -552,7 +557,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *in_pin,
return Table1(); return Table1();
} }
Table1 Waveform
CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin, CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin,
const RiseFall *in_rf, const RiseFall *in_rf,
const Pin *drvr_pin, const Pin *drvr_pin,

View File

@ -23,16 +23,20 @@ namespace sta {
using std::vector; using std::vector;
typedef map<const Pin*, FloatSeq, PinIdLess> WatchPinValuesMap;
ArcDelayCalc * ArcDelayCalc *
makeCcsCeffDelayCalc(StaState *sta); makeCcsCeffDelayCalc(StaState *sta);
class CcsCeffDelayCalc : public LumpedCapDelayCalc, public ArcDcalcWaveforms class CcsCeffDelayCalc : public LumpedCapDelayCalc,
public ArcDcalcWaveforms
{ {
public: public:
CcsCeffDelayCalc(StaState *sta); CcsCeffDelayCalc(StaState *sta);
virtual ~CcsCeffDelayCalc(); virtual ~CcsCeffDelayCalc();
ArcDelayCalc *copy() override; ArcDelayCalc *copy() override;
const char *name() const override { return "ccs_ceff"; }
bool reduceSupported() const override { return true; }
ArcDcalcResult gateDelay(const Pin *drvr_pin, ArcDcalcResult gateDelay(const Pin *drvr_pin,
const TimingArc *arc, const TimingArc *arc,
const Slew &in_slew, const Slew &in_slew,
@ -49,26 +53,11 @@ public:
const DcalcAnalysisPt *dcalc_ap, const DcalcAnalysisPt *dcalc_ap,
int digits) override; int digits) override;
Table1 drvrWaveform(const Pin *in_pin, // Record waveform for drvr/load pin.
const RiseFall *in_rf, void watchPin(const Pin *pin) override;
const Pin *drvr_pin, void clearWatchPins() override;
const RiseFall *drvr_rf, PinSeq watchPins() const override;
const Corner *corner, Waveform watchWaveform(const Pin *pin) override;
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;
protected: protected:
typedef vector<double> Region; typedef vector<double> Region;
@ -99,14 +88,23 @@ protected:
// Return values. // Return values.
ArcDelay &delay, ArcDelay &delay,
Slew &slew); Slew &slew);
double findVlTime(double v,
double elmore);
bool makeWaveformPreamble(const Pin *in_pin, bool makeWaveformPreamble(const Pin *in_pin,
const RiseFall *in_rf, const RiseFall *in_rf,
const Pin *drvr_pin, const Pin *drvr_pin,
const RiseFall *drvr_rf, const RiseFall *drvr_rf,
const Corner *corner, const Corner *corner,
const MinMax *min_max); const MinMax *min_max);
double findVlTime(double v, Waveform drvrWaveform();
double elmore); 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, void vl(double t,
double elmore, double elmore,
// Return values. // Return values.
@ -114,11 +112,10 @@ protected:
double &dvl_dt); double &dvl_dt);
double vl(double t, double vl(double t,
double elmore); double elmore);
Table1 drvrWaveform(const Slew &in_slew,
const RiseFall *drvr_rf);
void fail(const char *reason); void fail(const char *reason);
const Pin *drvr_pin_; const Pin *drvr_pin_;
const RiseFall *drvr_rf_;
double in_slew_; double in_slew_;
double load_cap_; double load_cap_;
const Parasitic *parasitic_; const Parasitic *parasitic_;
@ -148,6 +145,8 @@ protected:
Region region_ramp_times_; Region region_ramp_times_;
Region region_ramp_slopes_; Region region_ramp_slopes_;
bool vl_fail_; bool vl_fail_;
// Waveform recording.
WatchPinValuesMap watch_pin_values_;
const Unit *capacitance_unit_; const Unit *capacitance_unit_;
// Delay calculator to use when ccs waveforms are missing from liberty. // Delay calculator to use when ccs waveforms are missing from liberty.

View File

@ -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 <https://www.gnu.org/licenses/>.
#include "CcsSimDelayCalc.hh"
#include <cmath> // 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 *>(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<TableAxis>(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<TableAxis>(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<double> &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<double> &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

View File

@ -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 <https://www.gnu.org/licenses/>.
#pragma once
#include <vector>
#include <Eigen/SparseCore>
#include <Eigen/SparseLU>
#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<const Pin*, size_t, PinIdLess> PinNodeMap;
typedef Map<const ParasiticNode*, size_t> NodeIndexMap;
typedef Map<const Pin*, size_t> PortIndexMap;
typedef SparseMatrix<double> 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<double> &matrix);
void reportMatrix(MatrixSd &matrix);
void reportMatrix(MatrixXd &matrix);
void reportMatrix(VectorXd &matrix);
void reportVector(vector<double> &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<OutputWaveforms*> output_waveforms_;
vector<float> ref_time_;
double drive_resistance_;
double resistance_sum_;
vector<double> node_capacitances_;
bool includes_pin_caps_;
float coupling_cap_multiplier_;
// Indexed by driver index.
vector<double> ceff_;
vector<double> 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<MatrixSd> 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<double, measure_threshold_count_> ThresholdTimes;
// Vl Vth Vh
ThresholdTimes measure_thresholds_;
// Indexed by node number.
vector<ThresholdTimes> threshold_times_;
// Delay calculator to use when ccs waveforms are missing from liberty.
ArcDelayCalc *table_dcalc_;
using ArcDelayCalc::reduceParasitic;
};
} // namespacet

View File

@ -23,7 +23,6 @@
#include "DmpDelayCalc.hh" #include "DmpDelayCalc.hh"
#include "ArnoldiDelayCalc.hh" #include "ArnoldiDelayCalc.hh"
#include "CcsCeffDelayCalc.hh" #include "CcsCeffDelayCalc.hh"
#include "CcsSimDelayCalc.hh"
#include "PrimaDelayCalc.hh" #include "PrimaDelayCalc.hh"
namespace sta { namespace sta {
@ -41,7 +40,6 @@ registerDelayCalcs()
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc); registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
registerDelayCalc("arnoldi", makeArnoldiDelayCalc); registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc); registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc);
registerDelayCalc("ccs_sim", makeCcsSimDelayCalc);
registerDelayCalc("prima", makePrimaDelayCalc); registerDelayCalc("prima", makePrimaDelayCalc);
} }
@ -82,10 +80,8 @@ StringSeq
delayCalcNames() delayCalcNames()
{ {
StringSeq names; StringSeq names;
for (auto name_dcalc : *delay_calcs) { for (const auto [name, make_dcalc] : *delay_calcs)
const char *name = name_dcalc.first;
names.push_back(name); names.push_back(name);
}
return names; return names;
} }

View File

@ -1,7 +1,3 @@
%module dcalc
%{
// OpenSTA, Static Timing Analyzer // OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc. // Copyright (c) 2024, Parallax Software, Inc.
// //
@ -18,10 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Sta.hh" %module dcalc
%{
#include "DelayCalc.hh"
#include "ArcDelayCalc.hh" #include "ArcDelayCalc.hh"
#include "dcalc/ArcDcalcWaveforms.hh" #include "dcalc/ArcDcalcWaveforms.hh"
#include "dcalc/PrimaDelayCalc.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); 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<ArcDcalcWaveforms*>(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<ArcDcalcWaveforms*>(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<ArcDcalcWaveforms*>(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<ArcDcalcWaveforms*>(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 void
set_prima_reduce_order(size_t order) set_prima_reduce_order(size_t order)
{ {
cmdLinkedNetwork();
Sta *sta = Sta::sta(); Sta *sta = Sta::sta();
PrimaDelayCalc *dcalc = dynamic_cast<PrimaDelayCalc*>(sta->arcDelayCalc()); PrimaDelayCalc *dcalc = dynamic_cast<PrimaDelayCalc*>(sta->arcDelayCalc());
if (dcalc) { 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 %} // inline

View File

@ -148,17 +148,17 @@ proc set_assigned_delay { args } {
if [info exists keys(-from)] { if [info exists keys(-from)] {
set from_pins [get_port_pins_error "from_pins" $keys(-from)] set from_pins [get_port_pins_error "from_pins" $keys(-from)]
} else { } else {
sta_error 181 ""set_assigned_delay" missing -from argument." sta_error 181 "set_assigned_delay missing -from argument."
} }
if [info exists keys(-to)] { if [info exists keys(-to)] {
set to_pins [get_port_pins_error "to_pins" $keys(-to)] set to_pins [get_port_pins_error "to_pins" $keys(-to)]
} else { } else {
sta_error 182 ""set_assigned_delay" missing -to argument." sta_error 182 "set_assigned_delay missing -to argument."
} }
set delay [lindex $args 0] set delay [lindex $args 0]
if {![string is double $delay]} { 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] set delay [time_ui_sta $delay]
@ -283,7 +283,7 @@ proc set_assigned_check { args } {
} }
set check_value [lindex $args 0] set check_value [lindex $args 0]
if { ![string is double $check_value] } { 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] set check_value [time_ui_sta $check_value]

View File

@ -22,9 +22,11 @@
#include "TableModel.hh" #include "TableModel.hh"
#include "Network.hh" #include "Network.hh"
#include "Parasitics.hh" #include "Parasitics.hh"
#include "Graph.hh"
#include "Sdc.hh" #include "Sdc.hh"
#include "Corner.hh" #include "Corner.hh"
#include "DcalcAnalysisPt.hh" #include "DcalcAnalysisPt.hh"
#include "GraphDelayCalc.hh"
namespace sta { namespace sta {
@ -64,36 +66,6 @@ DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network,
delete pin_iter; 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<GateTimingModel*>(model(arc, dcalc_ap));
}
GateTableModel *
DelayCalcBase::gateTableModel(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<GateTableModel*>(model(arc, dcalc_ap));
}
CheckTimingModel *
DelayCalcBase::checkModel(const TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<CheckTimingModel*>(model(arc, dcalc_ap));
}
void void
DelayCalcBase::finishDrvrPin() DelayCalcBase::finishDrvrPin()
{ {
@ -114,10 +86,16 @@ DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin,
{ {
LibertyLibrary *load_library = thresholdLibrary(load_pin); LibertyLibrary *load_library = thresholdLibrary(load_pin);
float vth = load_library->inputThreshold(rf); float vth = 0.5;
float vl = load_library->slewLowerThreshold(rf); float vl = 0.2;
float vh = load_library->slewUpperThreshold(rf); float vh = 0.8;
float slew_derate = load_library->slewDerateFromLibrary(); 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); 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;
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, float related_out_cap,
const DcalcAnalysisPt *dcalc_ap) const DcalcAnalysisPt *dcalc_ap)
{ {
CheckTimingModel *model = checkModel(arc, dcalc_ap); CheckTimingModel *model = arc->checkModel(dcalc_ap);
if (model) { if (model) {
float from_slew1 = delayAsFloat(from_slew); float from_slew1 = delayAsFloat(from_slew);
float to_slew1 = delayAsFloat(to_slew); float to_slew1 = delayAsFloat(to_slew);
@ -197,7 +175,7 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin,
const DcalcAnalysisPt *dcalc_ap, const DcalcAnalysisPt *dcalc_ap,
int digits) int digits)
{ {
CheckTimingModel *model = checkModel(arc, dcalc_ap); CheckTimingModel *model = arc->checkModel(dcalc_ap);
if (model) { if (model) {
float from_slew1 = delayAsFloat(from_slew); float from_slew1 = delayAsFloat(from_slew);
float to_slew1 = delayAsFloat(to_slew); float to_slew1 = delayAsFloat(to_slew);
@ -219,4 +197,33 @@ DelayCalcBase::pinPvt(const Pin *pin,
return pvt; 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 } // namespace

View File

@ -22,6 +22,7 @@ namespace sta {
class GateTableModel; class GateTableModel;
// ArcDelayCalc helper functions.
class DelayCalcBase : public ArcDelayCalc class DelayCalcBase : public ArcDelayCalc
{ {
public: public:
@ -32,7 +33,10 @@ public:
const Net *net, const Net *net,
const Corner *corner, const Corner *corner,
const MinMaxAll *min_max) override; 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, ArcDelay checkDelay(const Pin *check_pin,
const TimingArc *arc, const TimingArc *arc,
const Slew &from_slew, const Slew &from_slew,
@ -50,14 +54,6 @@ public:
int digits) override; int digits) override;
protected: 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. // Find the liberty library to use for logic/slew thresholds.
LibertyLibrary *thresholdLibrary(const Pin *load_pin); LibertyLibrary *thresholdLibrary(const Pin *load_pin);
// Adjust load_delay and load_slew from driver thresholds to load thresholds. // Adjust load_delay and load_slew from driver thresholds to load thresholds.

View File

@ -1501,7 +1501,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
const LibertyCell *drvr_cell = arc->from()->libertyCell(); const LibertyCell *drvr_cell = arc->from()->libertyCell();
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); 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) { if (table_model && parasitic) {
float in_slew1 = delayAsFloat(in_slew); float in_slew1 = delayAsFloat(in_slew);
float c2, rpi, c1; float c2, rpi, c1;
@ -1516,9 +1516,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
dcalc_result.setGateDelay(gate_delay); dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew); dcalc_result.setDrvrSlew(drvr_slew);
for (auto load_pin_index : load_pin_index_map) { for (const auto [load_pin, load_idx] : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
ArcDelay wire_delay; ArcDelay wire_delay;
Slew load_slew; Slew load_slew;
loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic, loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic,
@ -1595,7 +1593,7 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
int digits) int digits)
{ {
gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, dcalc_ap); 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; float c_eff = 0.0;
string result; string result;
if (parasitic && dmp_alg_) { if (parasitic && dmp_alg_) {

View File

@ -34,6 +34,7 @@ class DmpCeffDelayCalc : public LumpedCapDelayCalc
public: public:
DmpCeffDelayCalc(StaState *sta); DmpCeffDelayCalc(StaState *sta);
virtual ~DmpCeffDelayCalc(); virtual ~DmpCeffDelayCalc();
bool reduceSupported() const override { return true; }
ArcDcalcResult gateDelay(const Pin *drvr_pin, ArcDcalcResult gateDelay(const Pin *drvr_pin,
const TimingArc *arc, const TimingArc *arc,
const Slew &in_slew, const Slew &in_slew,

View File

@ -36,6 +36,7 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc
public: public:
DmpCeffElmoreDelayCalc(StaState *sta); DmpCeffElmoreDelayCalc(StaState *sta);
ArcDelayCalc *copy() override; ArcDelayCalc *copy() override;
const char *name() const override { return "dmp_ceff_elmore"; }
ArcDcalcResult inputPortDelay(const Pin *port_pin, ArcDcalcResult inputPortDelay(const Pin *port_pin,
float in_slew, float in_slew,
const RiseFall *rf, const RiseFall *rf,
@ -81,9 +82,7 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *,
{ {
ArcDcalcResult dcalc_result(load_pin_index_map.size()); ArcDcalcResult dcalc_result(load_pin_index_map.size());
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
for (auto load_pin_index : load_pin_index_map) { for (auto [load_pin, load_idx] : 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; ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew; Slew load_slew = in_slew;
bool elmore_exists = false; bool elmore_exists = false;
@ -130,6 +129,7 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc
public: public:
DmpCeffTwoPoleDelayCalc(StaState *sta); DmpCeffTwoPoleDelayCalc(StaState *sta);
ArcDelayCalc *copy() override; ArcDelayCalc *copy() override;
const char *name() const override { return "dmp_ceff_two_pole"; }
Parasitic *findParasitic(const Pin *drvr_pin, Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
@ -257,9 +257,7 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *,
ArcDelay wire_delay = 0.0; ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew; Slew load_slew = in_slew;
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
for (auto load_pin_index : load_pin_index_map) { for (const auto [load_pin, load_idx] : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
if (parasitics_->isPiPoleResidue(parasitic)) { if (parasitics_->isPiPoleResidue(parasitic)) {
const Parasitic *pole_residue = parasitics_->findPoleResidue(parasitic, load_pin); const Parasitic *pole_residue = parasitics_->findPoleResidue(parasitic, load_pin);
if (pole_residue) { if (pole_residue) {

View File

@ -366,12 +366,12 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex,
float drive_res; float drive_res;
drive->driveResistance(rf, cnst_min_max, drive_res, exists); drive->driveResistance(rf, cnst_min_max, drive_res, exists);
const Parasitic *parasitic; const Parasitic *parasitic;
float cap; float load_cap;
parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc, parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc,
cap, parasitic); load_cap, parasitic);
if (exists) { if (exists) {
drive_delay = cap * drive_res; drive_delay = load_cap * drive_res;
slew = cap * drive_res; slew = load_cap * drive_res;
} }
const MinMax *slew_min_max = dcalc_ap->slewMinMax(); const MinMax *slew_min_max = dcalc_ap->slewMinMax();
if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) if (!drvr_vertex->slewAnnotated(rf, slew_min_max))
@ -796,7 +796,7 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex,
else else
initWireDelays(drvr_vertex); initWireDelays(drvr_vertex);
bool delay_changed = false; bool delay_changed = false;
bool has_delays = false; array<bool, RiseFall::index_count> delay_exists = {false, false};
VertexInEdgeIterator edge_iter(drvr_vertex, graph_); VertexInEdgeIterator edge_iter(drvr_vertex, graph_);
while (edge_iter.hasNext()) { while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next(); 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. // Don't let disabled edges set slews that influence downstream delays.
if (search_pred_->searchFrom(from_vertex) if (search_pred_->searchFrom(from_vertex)
&& search_pred_->searchThru(edge) && search_pred_->searchThru(edge)
&& !edge->role()->isLatchDtoQ()) { && !edge->role()->isLatchDtoQ())
delay_changed |= findDriverEdgeDelays(drvr_vertex, multi_drvr, edge, delay_changed |= findDriverEdgeDelays(drvr_vertex, multi_drvr, edge,
arc_delay_calc); arc_delay_calc, delay_exists);
has_delays = true; }
} for (auto rf : RiseFall::range()) {
if (!delay_exists[rf->index()])
zeroSlewAndWireDelays(drvr_vertex, rf);
} }
if (!has_delays)
zeroSlewAndWireDelays(drvr_vertex);
if (delay_changed && observer_) if (delay_changed && observer_)
observer_->delayChangedTo(drvr_vertex); observer_->delayChangedTo(drvr_vertex);
return delay_changed; return delay_changed;
@ -840,8 +840,9 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge)
Instance *drvr_inst = network_->instance(drvr_pin); Instance *drvr_inst = network_->instance(drvr_pin);
debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s", debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s",
sdc_network_->pathName(drvr_inst)); sdc_network_->pathName(drvr_inst));
array<bool, RiseFall::index_count> delay_exists = {false, false};
bool delay_changed = findDriverEdgeDelays(drvr_vertex, nullptr, edge, bool delay_changed = findDriverEdgeDelays(drvr_vertex, nullptr, edge,
arc_delay_calc_); arc_delay_calc_, delay_exists);
if (delay_changed && observer_) if (delay_changed && observer_)
observer_->delayChangedTo(drvr_vertex); observer_->delayChangedTo(drvr_vertex);
} }
@ -850,17 +851,20 @@ bool
GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex, GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex,
const MultiDrvrNet *multi_drvr, const MultiDrvrNet *multi_drvr,
Edge *edge, Edge *edge,
ArcDelayCalc *arc_delay_calc) ArcDelayCalc *arc_delay_calc,
array<bool, RiseFall::index_count> &delay_exists)
{ {
Vertex *from_vertex = edge->from(graph_); Vertex *from_vertex = edge->from(graph_);
const TimingArcSet *arc_set = edge->timingArcSet(); const TimingArcSet *arc_set = edge->timingArcSet();
bool delay_changed = false; bool delay_changed = false;
LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex);
for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { 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, delay_changed |= findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc,
load_pin_index_map, dcalc_ap, load_pin_index_map, dcalc_ap,
arc_delay_calc); arc_delay_calc);
delay_exists[arc->toEdge()->asRiseFall()->index()] = true;
}
} }
if (delay_changed && observer_) { if (delay_changed && observer_) {
observer_->delayChangedFrom(from_vertex); observer_->delayChangedFrom(from_vertex);
@ -908,8 +912,7 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex,
edge, arc, dcalc_ap, edge, arc, dcalc_ap,
arc_delay_calc); arc_delay_calc);
ArcDcalcResultSeq dcalc_results = ArcDcalcResultSeq dcalc_results =
arc_delay_calc->gateDelays(dcalc_args, load_cap, load_pin_index_map, arc_delay_calc->gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
dcalc_ap);
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) { for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx]; ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
ArcDcalcResult &dcalc_result = dcalc_results[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 RiseFall *drvr_rf = arc1->toEdge()->asRiseFall();
const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, dcalc_ap); const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, dcalc_ap);
const Pin *drvr_pin1 = drvr_vertex1->pin(); const Pin *drvr_pin1 = drvr_vertex1->pin();
Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin1, drvr_rf, float load_cap;
dcalc_ap); const Parasitic *parasitic;
dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, 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; return dcalc_args;
@ -1057,7 +1063,7 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge,
// Merge slews. // Merge slews.
const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index);
const MinMax *slew_min_max = dcalc_ap->slewMinMax(); 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) && !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)
&& !edge->role()->isLatchDtoQ()) && !edge->role()->isLatchDtoQ())
graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew);
@ -1328,30 +1334,29 @@ GraphDelayCalc::initSlew(Vertex *vertex)
} }
void void
GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex) GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex,
const RiseFall *rf)
{ {
for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { for (auto dcalc_ap : corners_->dcalcAnalysisPts()) {
DcalcAPIndex ap_index = dcalc_ap->index(); DcalcAPIndex ap_index = dcalc_ap->index();
const MinMax *slew_min_max = dcalc_ap->slewMinMax(); const MinMax *slew_min_max = dcalc_ap->slewMinMax();
for (auto rf : RiseFall::range()) { // Init drvr slew.
// Init drvr slew. if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) {
if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) { DcalcAPIndex ap_index = dcalc_ap->index();
DcalcAPIndex ap_index = dcalc_ap->index(); graph_->setSlew(drvr_vertex, rf, ap_index, slew_min_max->initValue());
graph_->setSlew(drvr_vertex, rf, ap_index, slew_min_max->initValue()); }
}
// Init wire delays and slews. // Init wire delays and slews.
VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); VertexOutEdgeIterator edge_iter(drvr_vertex, graph_);
while (edge_iter.hasNext()) { while (edge_iter.hasNext()) {
Edge *wire_edge = edge_iter.next(); Edge *wire_edge = edge_iter.next();
if (wire_edge->isWire()) { if (wire_edge->isWire()) {
Vertex *load_vertex = wire_edge->to(graph_); Vertex *load_vertex = wire_edge->to(graph_);
if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index))
graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0); graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0);
// Init load vertex slew. // Init load vertex slew.
if (!load_vertex->slewAnnotated(rf, slew_min_max)) if (!load_vertex->slewAnnotated(rf, slew_min_max))
graph_->setSlew(load_vertex, rf, ap_index, 0.0); 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 Slew
GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex, GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex,
const RiseFall *from_rf, const RiseFall *from_rf,
const Edge *edge, const Edge *edge,
const DcalcAnalysisPt *dcalc_ap) 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() if (role->genericRole() == TimingRole::regClkToQ()
&& clk_network_->isIdealClock(from_vertex->pin())) && clk_network_->isIdealClock(from_vertex->pin()))
return clk_network_->idealClkSlew(from_vertex->pin(), from_rf, return clk_network_->idealClkSlew(from_vertex->pin(), from_rf,

View File

@ -122,7 +122,7 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) const DcalcAnalysisPt *dcalc_ap)
{ {
GateTimingModel *model = gateModel(arc, dcalc_ap); GateTimingModel *model = arc->gateModel(dcalc_ap);
debugPrint(debug_, "delay_calc", 3, debugPrint(debug_, "delay_calc", 3,
" in_slew = %s load_cap = %s lumped", " in_slew = %s load_cap = %s lumped",
delayAsString(in_slew, this), delayAsString(in_slew, this),
@ -155,9 +155,7 @@ LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library,
dcalc_result.setGateDelay(gate_delay); dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew); dcalc_result.setDrvrSlew(drvr_slew);
for (auto load_pin_index : load_pin_index_map) { for (const auto [load_pin, load_idx] : 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; ArcDelay wire_delay = 0.0;
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, drvr_slew); thresholdAdjust(load_pin, drvr_library, rf, wire_delay, drvr_slew);
dcalc_result.setWireDelay(load_idx, wire_delay); dcalc_result.setWireDelay(load_idx, wire_delay);
@ -176,7 +174,7 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin,
const DcalcAnalysisPt *dcalc_ap, const DcalcAnalysisPt *dcalc_ap,
int digits) int digits)
{ {
GateTimingModel *model = gateModel(arc, dcalc_ap); GateTimingModel *model = arc->gateModel(dcalc_ap);
if (model) { if (model) {
float in_slew1 = delayAsFloat(in_slew); float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(check_pin, dcalc_ap), in_slew1, load_cap, return model->reportGateDelay(pinPvt(check_pin, dcalc_ap), in_slew1, load_cap,

View File

@ -27,9 +27,11 @@ class LumpedCapDelayCalc : public ParallelDelayCalc
public: public:
LumpedCapDelayCalc(StaState *sta); LumpedCapDelayCalc(StaState *sta);
ArcDelayCalc *copy() override; ArcDelayCalc *copy() override;
const char *name() const override { return "lumped_cap"; }
Parasitic *findParasitic(const Pin *drvr_pin, Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
bool reduceSupported() const override { return true; }
Parasitic *reduceParasitic(const Parasitic *parasitic_network, Parasitic *reduceParasitic(const Parasitic *parasitic_network,
const Pin *drvr_pin, const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,

View File

@ -33,26 +33,24 @@ ParallelDelayCalc::ParallelDelayCalc(StaState *sta):
ArcDcalcResultSeq ArcDcalcResultSeq
ParallelDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, ParallelDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) const DcalcAnalysisPt *dcalc_ap)
{ {
if (dcalc_args.size() == 1) { if (dcalc_args.size() == 1) {
ArcDcalcArg &dcalc_arg = dcalc_args[0]; ArcDcalcArg &dcalc_arg = dcalc_args[0];
ArcDcalcResult dcalc_result = gateDelay(dcalc_arg.drvrPin(), dcalc_arg.arc(), ArcDcalcResult dcalc_result = gateDelay(dcalc_arg.drvrPin(), dcalc_arg.arc(),
dcalc_arg.inSlew(), dcalc_arg.inSlew(), dcalc_arg.loadCap(),
load_cap, dcalc_arg.parasitic(), dcalc_arg.parasitic(),
load_pin_index_map, dcalc_ap); load_pin_index_map, dcalc_ap);
ArcDcalcResultSeq dcalc_results; ArcDcalcResultSeq dcalc_results;
dcalc_results.push_back(dcalc_result); dcalc_results.push_back(dcalc_result);
return dcalc_results; 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 ArcDcalcResultSeq
ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) const DcalcAnalysisPt *dcalc_ap)
{ {
@ -74,7 +72,7 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
ArcDelay intrinsic_delay = intrinsic_result.gateDelay(); ArcDelay intrinsic_delay = intrinsic_result.gateDelay();
intrinsic_delays[drvr_idx] = 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(), dcalc_arg.parasitic(),
load_pin_index_map, dcalc_ap); load_pin_index_map, dcalc_ap);
ArcDelay gate_delay = gate_result.gateDelay(); ArcDelay gate_delay = gate_result.gateDelay();
@ -88,8 +86,7 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
slew_sum += 1.0 / drvr_slew; slew_sum += 1.0 / drvr_slew;
dcalc_result.setLoadCount(load_pin_index_map.size()); dcalc_result.setLoadCount(load_pin_index_map.size());
for (auto load_pin_index : load_pin_index_map) { for (const auto [load_pin, load_idx] : load_pin_index_map) {
size_t load_idx = load_pin_index.second;
dcalc_result.setWireDelay(load_idx, gate_result.wireDelay(load_idx)); dcalc_result.setWireDelay(load_idx, gate_result.wireDelay(load_idx));
dcalc_result.setLoadSlew(load_idx, gate_result.loadSlew(load_idx)); dcalc_result.setLoadSlew(load_idx, gate_result.loadSlew(load_idx));
} }

View File

@ -29,12 +29,10 @@ class ParallelDelayCalc : public DelayCalcBase
public: public:
ParallelDelayCalc(StaState *sta); ParallelDelayCalc(StaState *sta);
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
protected: protected:
ArcDcalcResultSeq gateDelaysParallel(ArcDcalcArgSeq &dcalc_args, ArcDcalcResultSeq gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap); const DcalcAnalysisPt *dcalc_ap);
}; };

View File

@ -57,6 +57,7 @@ PrimaDelayCalc::PrimaDelayCalc(StaState *sta) :
dcalc_args_(nullptr), dcalc_args_(nullptr),
load_pin_index_map_(nullptr), load_pin_index_map_(nullptr),
pin_node_map_(network_), pin_node_map_(network_),
node_index_map_(ParasiticNodeLess(parasitics_, network_)),
prima_order_(3), prima_order_(3),
make_waveforms_(false), make_waveforms_(false),
waveform_drvr_pin_(nullptr), waveform_drvr_pin_(nullptr),
@ -71,6 +72,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) :
dcalc_args_(nullptr), dcalc_args_(nullptr),
load_pin_index_map_(nullptr), load_pin_index_map_(nullptr),
pin_node_map_(network_), pin_node_map_(network_),
node_index_map_(ParasiticNodeLess(parasitics_, network_)),
prima_order_(dcalc.prima_order_), prima_order_(dcalc.prima_order_),
make_waveforms_(false), make_waveforms_(false),
waveform_drvr_pin_(nullptr), waveform_drvr_pin_(nullptr),
@ -184,22 +186,19 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin,
const DcalcAnalysisPt *dcalc_ap) const DcalcAnalysisPt *dcalc_ap)
{ {
ArcDcalcArgSeq dcalc_args; ArcDcalcArgSeq dcalc_args;
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, parasitic); dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap, ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
load_pin_index_map, dcalc_ap);
return dcalc_results[0]; return dcalc_results[0];
} }
ArcDcalcResultSeq ArcDcalcResultSeq
PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) const DcalcAnalysisPt *dcalc_ap)
{ {
dcalc_args_ = &dcalc_args; dcalc_args_ = &dcalc_args;
load_pin_index_map_ = &load_pin_index_map; load_pin_index_map_ = &load_pin_index_map;
drvr_count_ = dcalc_args.size(); drvr_count_ = dcalc_args.size();
load_cap_ = load_cap;
dcalc_ap_ = dcalc_ap; dcalc_ap_ = dcalc_ap;
drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall(); drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall();
parasitic_network_ = dcalc_args[0].parasitic(); parasitic_network_ = dcalc_args[0].parasitic();
@ -208,14 +207,14 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
output_waveforms_.resize(drvr_count_); output_waveforms_.resize(drvr_count_);
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[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()) { if (table_model && dcalc_arg.parasitic()) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms(); OutputWaveforms *output_waveforms = table_model->outputWaveforms();
Slew in_slew = dcalc_arg.inSlew(); Slew in_slew = dcalc_arg.inSlew();
if (output_waveforms if (output_waveforms
// Bounds check because extrapolating waveforms does not work for shit. // Bounds check because extrapolating waveforms does not work for shit.
&& output_waveforms->slewAxis()->inBounds(in_slew) && 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; output_waveforms_[drvr_idx] = output_waveforms;
debugPrint(debug_, "ccs_dcalc", 1, "%s %s", debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
dcalc_arg.drvrCell()->name(), dcalc_arg.drvrCell()->name(),
@ -226,7 +225,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!vdd_exists) if (!vdd_exists)
report_->error(1720, "VDD not defined in library %s", drvr_library->name()); report_->error(1720, "VDD not defined in library %s", drvr_library->name());
drvr_cell->ensureVoltageWaveforms(); drvr_cell->ensureVoltageWaveforms(dcalc_ap);
if (drvr_idx == 0) { if (drvr_idx == 0) {
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
@ -241,7 +240,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
} }
if (failed) if (failed)
return tableDcalcResults(load_cap); return tableDcalcResults();
else { else {
simulate(); simulate();
return dcalcResults(); return dcalcResults();
@ -249,17 +248,18 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
} }
ArcDcalcResultSeq ArcDcalcResultSeq
PrimaDelayCalc::tableDcalcResults(float load_cap) PrimaDelayCalc::tableDcalcResults()
{ {
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) { for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx];
const Pin *drvr_pin = dcalc_arg.drvrPin(); const Pin *drvr_pin = dcalc_arg.drvrPin();
const RiseFall *rf = dcalc_arg.drvrEdge(); if (drvr_pin) {
const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, dcalc_ap_); const RiseFall *rf = dcalc_arg.drvrEdge();
dcalc_arg.setParasitic(parasitic); 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_, return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, dcalc_ap_);
dcalc_ap_);
} }
void void
@ -902,7 +902,7 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin,
const DcalcAnalysisPt *dcalc_ap, const DcalcAnalysisPt *dcalc_ap,
int digits) int digits)
{ {
GateTimingModel *model = gateModel(arc, dcalc_ap); GateTimingModel *model = arc->gateModel(dcalc_ap);
if (model) { if (model) {
float in_slew1 = delayAsFloat(in_slew); float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap, 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<TableAxis>(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<TableAxis>(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 void
PrimaDelayCalc::reportMatrix(const char *name, PrimaDelayCalc::reportMatrix(const char *name,
MatrixSd &matrix) MatrixSd &matrix)

View File

@ -24,6 +24,7 @@
#include "Map.hh" #include "Map.hh"
#include "LumpedCapDelayCalc.hh" #include "LumpedCapDelayCalc.hh"
#include "ArcDcalcWaveforms.hh" #include "ArcDcalcWaveforms.hh"
#include "Parasitics.hh"
namespace sta { namespace sta {
@ -41,7 +42,7 @@ using Eigen::Index;
using std::map; using std::map;
typedef Map<const Pin*, size_t, PinIdLess> PinNodeMap; typedef Map<const Pin*, size_t, PinIdLess> PinNodeMap;
typedef Map<const ParasiticNode*, size_t> NodeIndexMap; typedef map<const ParasiticNode*, size_t, ParasiticNodeLess> NodeIndexMap;
typedef Map<const Pin*, size_t> PortIndexMap; typedef Map<const Pin*, size_t> PortIndexMap;
typedef SparseMatrix<double> MatrixSd; typedef SparseMatrix<double> MatrixSd;
typedef Map<const Pin*, VectorXd, PinIdLess> PinLMap; typedef Map<const Pin*, VectorXd, PinIdLess> PinLMap;
@ -61,10 +62,12 @@ public:
~PrimaDelayCalc(); ~PrimaDelayCalc();
ArcDelayCalc *copy() override; ArcDelayCalc *copy() override;
void copyState(const StaState *sta) override; void copyState(const StaState *sta) override;
const char *name() const override { return "prima"; }
void setPrimaReduceOrder(size_t order); void setPrimaReduceOrder(size_t order);
Parasitic *findParasitic(const Pin *drvr_pin, Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
bool reduceSupported() const override { return false; }
Parasitic *reduceParasitic(const Parasitic *parasitic_network, Parasitic *reduceParasitic(const Parasitic *parasitic_network,
const Pin *drvr_pin, const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
@ -83,7 +86,6 @@ public:
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args, ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
string reportGateDelay(const Pin *drvr_pin, string reportGateDelay(const Pin *drvr_pin,
@ -96,31 +98,13 @@ public:
int digits) override; int digits) override;
// Record waveform for drvr/load pin. // Record waveform for drvr/load pin.
void watchPin(const Pin *pin); void watchPin(const Pin *pin) override;
void clearWatchPins(); void clearWatchPins() override;
PinSeq watchPins() const; PinSeq watchPins() const override;
Waveform watchWaveform(const Pin *pin); 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: protected:
ArcDcalcResultSeq tableDcalcResults(float load_cap); ArcDcalcResultSeq tableDcalcResults();
void simulate(); void simulate();
void simulate1(const MatrixSd &G, void simulate1(const MatrixSd &G,
const MatrixSd &C, const MatrixSd &C,

View File

@ -62,6 +62,18 @@ UnitDelayCalc::reduceParasitic(const Parasitic *,
{ {
} }
void
UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArg &,
const DcalcAnalysisPt *)
{
}
void
UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArgSeq &,
const DcalcAnalysisPt *)
{
}
ArcDcalcResult ArcDcalcResult
UnitDelayCalc::inputPortDelay(const Pin *, UnitDelayCalc::inputPortDelay(const Pin *,
float, float,
@ -87,7 +99,6 @@ UnitDelayCalc::gateDelay(const Pin *,
ArcDcalcResultSeq ArcDcalcResultSeq
UnitDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args, UnitDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
float,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *) const DcalcAnalysisPt *)
{ {

View File

@ -26,9 +26,11 @@ class UnitDelayCalc : public ArcDelayCalc
public: public:
UnitDelayCalc(StaState *sta); UnitDelayCalc(StaState *sta);
ArcDelayCalc *copy() override; ArcDelayCalc *copy() override;
const char *name() const override { return "unit"; }
Parasitic *findParasitic(const Pin *drvr_pin, Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
bool reduceSupported() const override { return false; }
Parasitic *reduceParasitic(const Parasitic *parasitic_network, Parasitic *reduceParasitic(const Parasitic *parasitic_network,
const Pin *drvr_pin, const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
@ -37,6 +39,10 @@ public:
const Net *net, const Net *net,
const Corner *corner, const Corner *corner,
const MinMaxAll *min_max) override; 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, ArcDcalcResult inputPortDelay(const Pin *port_pin,
float in_slew, float in_slew,
const RiseFall *rf, const RiseFall *rf,
@ -52,7 +58,6 @@ public:
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override; const DcalcAnalysisPt *dcalc_ap) override;
ArcDelay checkDelay(const Pin *check_pin, ArcDelay checkDelay(const Pin *check_pin,

View File

@ -1,8 +1,26 @@
OpenSTA Timing Analyzer Release Notes OpenSTA Timing Analyzer Release Notes
--------------------------------------------- -------------------------------------
This file summarizes user visible changes for each release. 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 Release 2.5.0 2024/01/17
------------------------- -------------------------
@ -24,6 +42,11 @@ timing groups.
report_clock_skew -include_internal_latency report_clock_skew -include_internal_latency
report_clock_latency -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 Release 2.4.0 2023/01/19
------------------------- -------------------------

View File

@ -8,6 +8,7 @@ member variable - snake case with trailing underscore (member_variable_)
Trailing underscore prevents conflict with accessor Trailing underscore prevents conflict with accessor
member function name. member function name.
function - lower camel case (functionName) function - lower camel case (functionName)
variable - snake case
comments - use capitalized sentences that end with periods comments - use capitalized sentences that end with periods
C++ code files should use a .cc file extension C++ code files should use a .cc file extension
@ -55,7 +56,7 @@ this:
instead, write this: instead, write this:
foo = (char *) malloc (sizeof *foo); foo = (char *) malloc (sizeof *foo);
if (foo == 0) if (foo == nullptr)
fatal ("virtual memory exhausted"); fatal ("virtual memory exhausted");

Binary file not shown.

Binary file not shown.

View File

@ -757,10 +757,8 @@ Graph::makeArcDelayTables(DcalcAPIndex ap_count)
{ {
if (have_arc_delays_) { if (have_arc_delays_) {
arc_delays_.resize(ap_count); arc_delays_.resize(ap_count);
for (DcalcAPIndex i = 0; i < ap_count; i++) { for (DcalcAPIndex i = 0; i < ap_count; i++)
DelayTable *table = new DelayTable(); arc_delays_[i] = new DelayTable();
arc_delays_[i] = table;
}
} }
} }
@ -1041,7 +1039,7 @@ Graph::setPeriodCheckAnnotation(const Pin *pin,
float period) float period)
{ {
if (period_check_annotations_ == nullptr) if (period_check_annotations_ == nullptr)
period_check_annotations_ = new PeriodCheckAnnotations; period_check_annotations_ = new PeriodCheckAnnotations(network_);
float *periods = period_check_annotations_->findKey(pin); float *periods = period_check_annotations_->findKey(pin);
if (periods == nullptr) { if (periods == nullptr) {
periods = new float[ap_count_]; periods = new float[ap_count_];
@ -1057,10 +1055,8 @@ void
Graph::removePeriodCheckAnnotations() Graph::removePeriodCheckAnnotations()
{ {
if (period_check_annotations_) { if (period_check_annotations_) {
for (auto pin_floats : *period_check_annotations_) { for (const auto [pin, periods] : *period_check_annotations_)
float *periods = pin_floats.second;
delete [] periods; delete [] periods;
}
delete period_check_annotations_; delete period_check_annotations_;
period_check_annotations_ = nullptr; period_check_annotations_ = nullptr;
} }

459
graph/Graph.i Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
%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; }
}

View File

@ -37,7 +37,7 @@ VertexNameLess::operator()(const Vertex *vertex1,
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
EdgeLess::EdgeLess(const Network *network, EdgeLess::EdgeLess(const Network *network,
Graph *graph) : Graph *&graph) :
pin_less_(network), pin_less_(network),
graph_(graph) graph_(graph)
{ {

View File

@ -40,6 +40,10 @@ class Corner;
class Parasitic; class Parasitic;
class DcalcAnalysisPt; class DcalcAnalysisPt;
class MultiDrvrNet; class MultiDrvrNet;
class ArcDcalcArg;
typedef std::vector<ArcDcalcArg*> ArcDcalcArgPtrSeq;
typedef std::vector<ArcDcalcArg> ArcDcalcArgSeq;
// Driver load pin -> index in driver loads. // Driver load pin -> index in driver loads.
typedef map<const Pin *, size_t, PinIdLess> LoadPinIndexMap; typedef map<const Pin *, size_t, PinIdLess> LoadPinIndexMap;
@ -56,6 +60,7 @@ public:
Edge *edge, Edge *edge,
const TimingArc *arc, const TimingArc *arc,
const Slew in_slew, const Slew in_slew,
float load_cap,
const Parasitic *parasitic); const Parasitic *parasitic);
ArcDcalcArg(const Pin *in_pin, ArcDcalcArg(const Pin *in_pin,
const Pin *drvr_pin, const Pin *drvr_pin,
@ -65,6 +70,7 @@ public:
const Pin *inPin() const { return in_pin_; } const Pin *inPin() const { return in_pin_; }
const RiseFall *inEdge() const; const RiseFall *inEdge() const;
const Pin *drvrPin() const { return drvr_pin_; } const Pin *drvrPin() const { return drvr_pin_; }
Vertex *drvrVertex(const Graph *graph) const;
LibertyCell *drvrCell() const; LibertyCell *drvrCell() const;
const LibertyLibrary *drvrLibrary() const; const LibertyLibrary *drvrLibrary() const;
const RiseFall *drvrEdge() const; const RiseFall *drvrEdge() const;
@ -74,9 +80,12 @@ public:
Slew inSlew() const { return in_slew_; } Slew inSlew() const { return in_slew_; }
float inSlewFlt() const; float inSlewFlt() const;
void setInSlew(Slew in_slew); void setInSlew(Slew in_slew);
const Parasitic *parasitic() { return parasitic_; } const Parasitic *parasitic() const { return parasitic_; }
void setParasitic(const Parasitic *parasitic); void setParasitic(const Parasitic *parasitic);
float loadCap() const { return load_cap_; }
void setLoadCap(float load_cap);
float inputDelay() const { return input_delay_; } float inputDelay() const { return input_delay_; }
void setInputDelay(float input_delay);
protected: protected:
const Pin *in_pin_; const Pin *in_pin_;
@ -84,10 +93,21 @@ protected:
Edge *edge_; Edge *edge_;
const TimingArc *arc_; const TimingArc *arc_;
Slew in_slew_; Slew in_slew_;
float load_cap_;
const Parasitic *parasitic_; const Parasitic *parasitic_;
float input_delay_; 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. // Arc delay calc result.
class ArcDcalcResult class ArcDcalcResult
{ {
@ -127,6 +147,9 @@ typedef vector<ArcDcalcResult> ArcDcalcResultSeq;
// DmpCeffElmoreDelayCalc // DmpCeffElmoreDelayCalc
// DmpCeffTwoPoleDelayCalc // DmpCeffTwoPoleDelayCalc
// ArnoldiDelayCalc // ArnoldiDelayCalc
// CcsCeffDelayCalc
// CcsSimfDelayCalc
// PrimafDelayCalc
// Abstract class for the graph delay calculator traversal to interface // Abstract class for the graph delay calculator traversal to interface
// to a delay calculator primitive. // to a delay calculator primitive.
@ -136,12 +159,14 @@ public:
explicit ArcDelayCalc(StaState *sta); explicit ArcDelayCalc(StaState *sta);
virtual ~ArcDelayCalc() {} virtual ~ArcDelayCalc() {}
virtual ArcDelayCalc *copy() = 0; virtual ArcDelayCalc *copy() = 0;
virtual const char *name() const = 0;
// Find the parasitic for drvr_pin that is acceptable to the delay // Find the parasitic for drvr_pin that is acceptable to the delay
// calculator by probing parasitics_. // calculator by probing parasitics_.
virtual Parasitic *findParasitic(const Pin *drvr_pin, virtual Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) = 0; const DcalcAnalysisPt *dcalc_ap) = 0;
virtual bool reduceSupported() const = 0;
// Reduce parasitic_network to a representation acceptable to the delay calculator. // Reduce parasitic_network to a representation acceptable to the delay calculator.
virtual Parasitic *reduceParasitic(const Parasitic *parasitic_network, virtual Parasitic *reduceParasitic(const Parasitic *parasitic_network,
const Pin *drvr_pin, const Pin *drvr_pin,
@ -154,6 +179,11 @@ public:
const Net *net, const Net *net,
const Corner *corner, const Corner *corner,
const MinMaxAll *min_max) = 0; 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. // Find the wire delays and slews for an input port without a driving cell.
// This call primarily initializes the load delay/slew iterator. // This call primarily initializes the load delay/slew iterator.
virtual ArcDcalcResult inputPortDelay(const Pin *port_pin, virtual ArcDcalcResult inputPortDelay(const Pin *port_pin,
@ -172,6 +202,7 @@ public:
const Parasitic *parasitic, const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) = 0; const DcalcAnalysisPt *dcalc_ap) = 0;
// deprecated 2024-02-27
virtual void gateDelay(const TimingArc *arc, virtual void gateDelay(const TimingArc *arc,
const Slew &in_slew, const Slew &in_slew,
float load_cap, float load_cap,
@ -185,7 +216,6 @@ public:
// Find gate delays and slews for parallel gates. // Find gate delays and slews for parallel gates.
virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args, virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map, const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) = 0; const DcalcAnalysisPt *dcalc_ap) = 0;

View File

@ -21,12 +21,8 @@
#include "StaState.hh" #include "StaState.hh"
#include "LibertyClass.hh" #include "LibertyClass.hh"
#if CUDD
#include "cudd.h"
#else
struct DdNode; struct DdNode;
struct DdManager; struct DdManager;
#endif
namespace sta { namespace sta {

View File

@ -49,8 +49,8 @@ template <class ENUM>
EnumNameMap<ENUM>::EnumNameMap(initializer_list<pair<const ENUM, string>> enum_names) : EnumNameMap<ENUM>::EnumNameMap(initializer_list<pair<const ENUM, string>> enum_names) :
enum_map_(enum_names) enum_map_(enum_names)
{ {
for (auto iter = enum_map_.begin(); iter != enum_map_.end(); iter++) for (const auto& [key, name] : enum_map_)
name_map_[iter->second] = iter->first; name_map_[name] = key;
} }
template <class ENUM> template <class ENUM>

View File

@ -49,6 +49,8 @@ public:
int priority, int priority,
const char *comment); const char *comment);
virtual ~ExceptionPath(); virtual ~ExceptionPath();
size_t id() const { return id_; }
void setId(size_t id);
virtual bool isFalse() const { return false; } virtual bool isFalse() const { return false; }
virtual bool isLoop() const { return false; } virtual bool isLoop() const { return false; }
virtual bool isMultiCycle() const { return false; } virtual bool isMultiCycle() const { return false; }
@ -128,6 +130,7 @@ protected:
const MinMaxAll *min_max_; const MinMaxAll *min_max_;
bool own_pts_; bool own_pts_;
int priority_; int priority_;
size_t id_; // Unique ID assigned by Sdc.
ExceptionState *states_; ExceptionState *states_;
}; };

View File

@ -46,7 +46,7 @@ typedef ArrayTable<Required> RequiredsTable;
typedef ArrayTable<PathVertexRep> PrevPathsTable; typedef ArrayTable<PathVertexRep> PrevPathsTable;
typedef Map<const Pin*, Vertex*> PinVertexMap; typedef Map<const Pin*, Vertex*> PinVertexMap;
typedef Iterator<Edge*> VertexEdgeIterator; typedef Iterator<Edge*> VertexEdgeIterator;
typedef Map<const Pin*, float*> PeriodCheckAnnotations; typedef Map<const Pin*, float*, PinIdLess> PeriodCheckAnnotations;
typedef Vector<DelayTable*> DelayTableSeq; typedef Vector<DelayTable*> DelayTableSeq;
typedef ObjectId EdgeId; typedef ObjectId EdgeId;
typedef ObjectId ArrivalId; typedef ObjectId ArrivalId;
@ -352,13 +352,13 @@ protected:
EdgeId in_edges_; // Edges to this vertex. EdgeId in_edges_; // Edges to this vertex.
EdgeId out_edges_; // Edges from this vertex. EdgeId out_edges_; // Edges from this vertex.
// 4 bytes // 32 bits
unsigned int tag_group_index_:tag_group_index_bits; // 24 unsigned int tag_group_index_:tag_group_index_bits; // 24
// Each bit corresponds to a different BFS queue. // Each bit corresponds to a different BFS queue.
unsigned int bfs_in_queue_:int(BfsIndex::bits); // 4 unsigned int bfs_in_queue_:int(BfsIndex::bits); // 4
unsigned int slew_annotated_:slew_annotated_bits; unsigned int slew_annotated_:slew_annotated_bits;
// 4 bytes (32 bits) // 32 bits
unsigned int level_:Graph::vertex_level_bits; unsigned int level_:Graph::vertex_level_bits;
// Levelization search state. // Levelization search state.
// LevelColor gcc barfs if this is dcl'd. // LevelColor gcc barfs if this is dcl'd.
@ -369,6 +369,8 @@ protected:
// This flag distinguishes the driver and load vertices. // This flag distinguishes the driver and load vertices.
bool is_bidirect_drvr_:1; bool is_bidirect_drvr_:1;
bool is_reg_clk_:1; bool is_reg_clk_:1;
// 15 bits
bool is_disabled_constraint_:1; bool is_disabled_constraint_:1;
bool is_gated_clk_enable_:1; bool is_gated_clk_enable_:1;
// Constrained by timing check edge. // Constrained by timing check edge.
@ -379,7 +381,6 @@ protected:
bool has_downstream_clk_pin_:1; bool has_downstream_clk_pin_:1;
bool crpr_path_pruning_disabled_:1; bool crpr_path_pruning_disabled_:1;
bool requireds_pruned_:1; bool requireds_pruned_:1;
unsigned object_idx_:VertexTable::idx_bits; unsigned object_idx_:VertexTable::idx_bits;
private: private:
@ -441,6 +442,7 @@ protected:
EdgeId vertex_out_next_; // Vertex out edges doubly linked list. EdgeId vertex_out_next_; // Vertex out edges doubly linked list.
EdgeId vertex_out_prev_; EdgeId vertex_out_prev_;
ArcId arc_delays_; ArcId arc_delays_;
// 16 bits
bool delay_annotation_is_incremental_:1; bool delay_annotation_is_incremental_:1;
bool is_bidirect_inst_path_:1; bool is_bidirect_inst_path_:1;
bool is_bidirect_net_path_:1; bool is_bidirect_net_path_:1;

View File

@ -37,13 +37,13 @@ class EdgeLess
{ {
public: public:
EdgeLess(const Network *network, EdgeLess(const Network *network,
Graph *graph); Graph *&graph);
bool operator()(const Edge *edge1, bool operator()(const Edge *edge1,
const Edge *edge2) const; const Edge *edge2) const;
private: private:
const PinPathNameLess pin_less_; const PinPathNameLess pin_less_;
Graph *graph_; Graph *&graph_;
}; };
void void

View File

@ -18,6 +18,7 @@
#include <vector> #include <vector>
#include <mutex> #include <mutex>
#include <array>
#include "Map.hh" #include "Map.hh"
#include "NetworkClass.hh" #include "NetworkClass.hh"
@ -32,6 +33,7 @@ namespace sta {
using std::vector; using std::vector;
using std::map; using std::map;
using std::array;
class DelayCalcObserver; class DelayCalcObserver;
class MultiDrvrNet; class MultiDrvrNet;
@ -118,6 +120,10 @@ public:
const RiseFall *from_rf, const RiseFall *from_rf,
const Edge *edge, const Edge *edge,
const DcalcAnalysisPt *dcalc_ap); const DcalcAnalysisPt *dcalc_ap);
Slew edgeFromSlew(const Vertex *from_vertex,
const RiseFall *from_rf,
const TimingRole *role,
const DcalcAnalysisPt *dcalc_ap);
protected: protected:
void seedInvalidDelays(); void seedInvalidDelays();
@ -171,7 +177,8 @@ protected:
bool findDriverEdgeDelays(Vertex *drvr_vertex, bool findDriverEdgeDelays(Vertex *drvr_vertex,
const MultiDrvrNet *multi_drvr, const MultiDrvrNet *multi_drvr,
Edge *edge, Edge *edge,
ArcDelayCalc *arc_delay_calc); ArcDelayCalc *arc_delay_calc,
array<bool, RiseFall::index_count> &delay_exists);
bool findDriverArcDelays(Vertex *drvr_vertex, bool findDriverArcDelays(Vertex *drvr_vertex,
const MultiDrvrNet *multi_drvr, const MultiDrvrNet *multi_drvr,
Edge *edge, Edge *edge,
@ -193,7 +200,8 @@ protected:
const TimingArc *&arc); const TimingArc *&arc);
void initWireDelays(Vertex *drvr_vertex); void initWireDelays(Vertex *drvr_vertex);
void initRootSlews(Vertex *vertex); void initRootSlews(Vertex *vertex);
void zeroSlewAndWireDelays(Vertex *drvr_vertex); void zeroSlewAndWireDelays(Vertex *drvr_vertex,
const RiseFall *rf);
void findVertexDelay(Vertex *vertex, void findVertexDelay(Vertex *vertex,
ArcDelayCalc *arc_delay_calc, ArcDelayCalc *arc_delay_calc,
bool propagate); bool propagate);

View File

@ -73,7 +73,8 @@ typedef Map<const OperatingConditions*, LibertyCell*> ScaledCellMap;
typedef Map<const OperatingConditions*, LibertyPort*> ScaledPortMap; typedef Map<const OperatingConditions*, LibertyPort*> ScaledPortMap;
typedef Map<const char *, ModeDef*, CharPtrLess> ModeDefMap; typedef Map<const char *, ModeDef*, CharPtrLess> ModeDefMap;
typedef Map<const char *, ModeValueDef*, CharPtrLess> ModeValueMap; typedef Map<const char *, ModeValueDef*, CharPtrLess> ModeValueMap;
typedef Map<TimingArcSet*, LatchEnable*> LatchEnableMap; typedef Map<const TimingArcSet*, LatchEnable*> LatchEnableMap;
typedef Vector<LatchEnable*> LatchEnableSeq;
typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap; typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap;
typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq; typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq;
typedef Map<const char *, float, CharPtrLess> SupplyVoltageMap; typedef Map<const char *, float, CharPtrLess> SupplyVoltageMap;
@ -472,12 +473,12 @@ public:
bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; } bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; }
TestCell *testCell() const { return test_cell_; } TestCell *testCell() const { return test_cell_; }
bool isLatchData(LibertyPort *port); bool isLatchData(LibertyPort *port);
void latchEnable(TimingArcSet *arc_set, void latchEnable(const TimingArcSet *arc_set,
// Return values. // Return values.
LibertyPort *&enable_port, const LibertyPort *&enable_port,
FuncExpr *&enable_func, const FuncExpr *&enable_func,
RiseFall *&enable_rf) const; const RiseFall *&enable_rf) const;
RiseFall *latchCheckEnableEdge(TimingArcSet *check_set); const RiseFall *latchCheckEnableEdge(TimingArcSet *check_set);
bool isDisabledConstraint() const { return is_disabled_constraint_; } bool isDisabledConstraint() const { return is_disabled_constraint_; }
LibertyCell *cornerCell(const Corner *corner, LibertyCell *cornerCell(const Corner *corner,
const MinMax *min_max); const MinMax *min_max);
@ -531,7 +532,7 @@ public:
// Check all liberty cells to make sure they exist // Check all liberty cells to make sure they exist
// for all the defined corners. // for all the defined corners.
static void checkLibertyCorners(); static void checkLibertyCorners();
void ensureVoltageWaveforms(); void ensureVoltageWaveforms(const DcalcAnalysisPt *dcalc_ap);
protected: protected:
void addPort(ConcretePort *port); void addPort(ConcretePort *port);
@ -539,18 +540,26 @@ protected:
void setLibertyLibrary(LibertyLibrary *library); void setLibertyLibrary(LibertyLibrary *library);
void makeLatchEnables(Report *report, void makeLatchEnables(Report *report,
Debug *debug); Debug *debug);
FuncExpr *findLatchEnableFunc(LibertyPort *data, FuncExpr *findLatchEnableFunc(const LibertyPort *d,
LibertyPort *enable) const; const LibertyPort *en,
const RiseFall *en_rf) const;
LatchEnable *makeLatchEnable(LibertyPort *d, LatchEnable *makeLatchEnable(LibertyPort *d,
LibertyPort *en, LibertyPort *en,
const RiseFall *en_rf,
LibertyPort *q, LibertyPort *q,
TimingArcSet *d_to_q, TimingArcSet *d_to_q,
TimingArcSet *en_to_q, TimingArcSet *en_to_q,
TimingArcSet *setup_check, TimingArcSet *setup_check,
Debug *debug); Debug *debug);
TimingArcSet *findLatchSetup(const LibertyPort *d,
const LibertyPort *en,
const RiseFall *en_rf,
const LibertyPort *q,
Report *report);
void findDefaultCondArcs(); void findDefaultCondArcs();
void translatePresetClrCheckRoles(); void translatePresetClrCheckRoles();
void inferLatchRoles(Debug *debug); void inferLatchRoles(Report *report,
Debug *debug);
void deleteInternalPowerAttrs(); void deleteInternalPowerAttrs();
void makeTimingArcMap(Report *report); void makeTimingArcMap(Report *report);
void makeTimingArcPortMaps(); void makeTimingArcPortMaps();
@ -596,6 +605,7 @@ protected:
LatchEnableMap latch_d_to_q_map_; LatchEnableMap latch_d_to_q_map_;
// Latch EN->D setup to LatchEnable. // Latch EN->D setup to LatchEnable.
LatchEnableMap latch_check_map_; LatchEnableMap latch_check_map_;
LatchEnableSeq latch_enables_;
// Ports that have latch D->Q timing arc sets from them. // Ports that have latch D->Q timing arc sets from them.
LibertyPortSet latch_data_ports_; LibertyPortSet latch_data_ports_;
float ocv_arc_depth_; float ocv_arc_depth_;
@ -792,20 +802,20 @@ public:
DriverWaveform *driverWaveform(const RiseFall *rf) const; DriverWaveform *driverWaveform(const RiseFall *rf) const;
void setDriverWaveform(DriverWaveform *driver_waveform, void setDriverWaveform(DriverWaveform *driver_waveform,
const RiseFall *rf); 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, float clkTreeDelay(float in_slew,
const RiseFall *from_rf, const RiseFall *from_rf,
const RiseFall *to_rf, const RiseFall *to_rf,
const MinMax *min_max) const; const MinMax *min_max) const;
// Assumes input slew of 0.0. float clkTreeDelay(float in_slew,
RiseFallMinMax clkTreeDelays() const; 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)); RiseFallMinMax clockTreePathDelays() const __attribute__ ((deprecated));
static bool equiv(const LibertyPort *port1, static bool equiv(const LibertyPort *port1,

View File

@ -101,9 +101,7 @@ public:
void void
deleteKeysContents() deleteKeysContents()
{ {
for (auto key_value : this) { for (const auto [key, value] : this) {
KEY key = key_value.first;
VALUE value = key_value.second;
delete key; delete key;
delete value; delete value;
} }

View File

@ -108,9 +108,9 @@ public:
bool matches(const MinMax *min_max) const; bool matches(const MinMax *min_max) const;
bool matches(const MinMaxAll *min_max) const; bool matches(const MinMaxAll *min_max) const;
static MinMaxAll *find(const char *min_max); 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<MinMax*> &range() const { return range_; } const std::vector<MinMax*> &range() const { return range_; }
// for (auto mm_index : min_max->rangeIndex()) {} // for (const auto mm_index : min_max->rangeIndex()) {}
const std::vector<int> &rangeIndex() const { return range_index_; } const std::vector<int> &rangeIndex() const { return range_index_; }
private: private:

View File

@ -513,6 +513,7 @@ public:
LibertyPort *port, LibertyPort *port,
Net *net) = 0; Net *net) = 0;
// makePin/connectPin replaced by connect. // makePin/connectPin replaced by connect.
// deprecated 2018-09-28
virtual void connectPin(Pin *pin, virtual void connectPin(Pin *pin,
Net *net) __attribute__ ((deprecated)); Net *net) __attribute__ ((deprecated));
// Disconnect pin from net. // Disconnect pin from net.

View File

@ -175,6 +175,7 @@ public:
// Find the parasitic node connected to pin. // Find the parasitic node connected to pin.
virtual ParasiticNode *findParasiticNode(const Parasitic *parasitic, virtual ParasiticNode *findParasiticNode(const Parasitic *parasitic,
const Pin *pin) const = 0; const Pin *pin) const = 0;
// deprecated 2024-02-27
virtual ParasiticNode *findNode(const Parasitic *parasitic, virtual ParasiticNode *findNode(const Parasitic *parasitic,
const Pin *pin) const __attribute__ ((deprecated)); const Pin *pin) const __attribute__ ((deprecated));
// Make a subnode of the parasitic network net connected to pin. // 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 Pin *pin(const ParasiticNode *node) const = 0;
virtual const Net *net(const ParasiticNode *node, virtual const Net *net(const ParasiticNode *node,
const Network *network) const = 0; const Network *network) const = 0;
virtual unsigned netId(const ParasiticNode *node) const = 0;
virtual bool isExternal(const ParasiticNode *node) const = 0; virtual bool isExternal(const ParasiticNode *node) const = 0;
// Node capacitance to ground. // Node capacitance to ground.
virtual float nodeGndCap(const ParasiticNode *node) const = 0; virtual float nodeGndCap(const ParasiticNode *node) const = 0;
@ -308,4 +310,17 @@ private:
float coupling_cap_factor_; 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 } // namespace

View File

@ -136,7 +136,8 @@ public:
virtual PathDelay *pathDelay() const; virtual PathDelay *pathDelay() const;
// This returns the crpr signed with respect to the check type. // This returns the crpr signed with respect to the check type.
// Positive for setup, negative for hold. // 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 MultiCyclePath *multiCyclePath() const;
virtual TimingArc *checkArc() const { return nullptr; } virtual TimingArc *checkArc() const { return nullptr; }
// PathEndDataCheck data clock path. // PathEndDataCheck data clock path.
@ -244,7 +245,7 @@ public:
virtual float targetNonInterClkUncertainty(const StaState *sta) const; virtual float targetNonInterClkUncertainty(const StaState *sta) const;
virtual float interClkUncertainty(const StaState *sta) const; virtual float interClkUncertainty(const StaState *sta) const;
virtual float targetClkUncertainty(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 Required requiredTime(const StaState *sta) const;
virtual Slack slack(const StaState *sta) const; virtual Slack slack(const StaState *sta) const;
virtual Slack slackNoCrpr(const StaState *sta) const; virtual Slack slackNoCrpr(const StaState *sta) const;
@ -426,7 +427,7 @@ public:
virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const; virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const;
virtual Delay targetClkDelay(const StaState *sta) const; virtual Delay targetClkDelay(const StaState *sta) const;
virtual Delay targetClkInsertionDelay(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, virtual int exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const; const StaState *sta) const;

View File

@ -200,6 +200,8 @@ public:
void deleteNetBefore(const Net *net); void deleteNetBefore(const Net *net);
// SWIG sdc interface. // SWIG sdc interface.
PortSeq allInputs(bool no_clks);
PortSeq allOutputs();
AnalysisType analysisType() { return analysis_type_; } AnalysisType analysisType() { return analysis_type_; }
void setAnalysisType(AnalysisType analysis_type); void setAnalysisType(AnalysisType analysis_type);
void setOperatingConditions(OperatingConditions *op_cond, void setOperatingConditions(OperatingConditions *op_cond,
@ -953,6 +955,13 @@ public:
float &wire_cap, float &wire_cap,
float &fanout, float &fanout,
bool &has_net_load) const; 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, void portExtFanout(const Port *port,
const Corner *corner, const Corner *corner,
const MinMax *min_max, const MinMax *min_max,
@ -1046,6 +1055,8 @@ public:
bool bidirectDrvrSlewFromLoad(const Pin *pin) const; bool bidirectDrvrSlewFromLoad(const Pin *pin) const;
protected: protected:
void portMembers(const Port *port,
PortSeq &ports);
void initVariables(); void initVariables();
void clearCycleAcctings(); void clearCycleAcctings();
void removeLibertyAnnotations(); void removeLibertyAnnotations();
@ -1231,13 +1242,6 @@ protected:
void annotateHierClkLatency(); void annotateHierClkLatency();
void annotateHierClkLatency(const Pin *hpin, void annotateHierClkLatency(const Pin *hpin,
ClockLatency *latency); 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, void netCaps(const Pin *drvr_pin,
const RiseFall *rf, const RiseFall *rf,
const Corner *corner, const Corner *corner,
@ -1252,7 +1256,8 @@ protected:
const RiseFall *rf, const RiseFall *rf,
const Corner *corner, const Corner *corner,
const MinMax *min_max); const MinMax *min_max);
float portCapacitance(Instance *inst, LibertyPort *port, float portCapacitance(Instance *inst,
LibertyPort *port,
const RiseFall *rf, const RiseFall *rf,
const Corner *corner, const Corner *corner,
const MinMax *min_max) const; const MinMax *min_max) const;
@ -1364,6 +1369,7 @@ protected:
InstanceSet disabled_clk_gating_checks_inst_; InstanceSet disabled_clk_gating_checks_inst_;
PinSet disabled_clk_gating_checks_pin_; PinSet disabled_clk_gating_checks_pin_;
ExceptionPathSet exceptions_; ExceptionPathSet exceptions_;
size_t exception_id_; // Unique ID for exceptions.
bool have_thru_hpin_exceptions_; bool have_thru_hpin_exceptions_;
// First pin/clock/instance/net/edge exception point to exception set map. // First pin/clock/instance/net/edge exception point to exception set map.

View File

@ -85,7 +85,7 @@ typedef MinMaxAll SetupHoldAll;
typedef Vector<ExceptionThru*> ExceptionThruSeq; typedef Vector<ExceptionThru*> ExceptionThruSeq;
typedef Set<LibertyPortPair, LibertyPortPairLess> LibertyPortPairSet; typedef Set<LibertyPortPair, LibertyPortPairLess> LibertyPortPairSet;
typedef Map<const Instance*, DisabledInstancePorts*> DisabledInstancePortsMap; typedef Map<const Instance*, DisabledInstancePorts*> DisabledInstancePortsMap;
typedef Map<const LibertyCell*, DisabledCellPorts*> DisabledCellPortsMap; typedef Map<LibertyCell*, DisabledCellPorts*> DisabledCellPortsMap;
typedef MinMaxValues<float> ClockUncertainties; typedef MinMaxValues<float> ClockUncertainties;
typedef Set<ExceptionPath*> ExceptionPathSet; typedef Set<ExceptionPath*> ExceptionPathSet;
typedef PinPair EdgePins; typedef PinPair EdgePins;
@ -107,12 +107,8 @@ class ExceptionState;
class ExceptionStateLess class ExceptionStateLess
{ {
public: public:
ExceptionStateLess(const Network *network);
bool operator()(const ExceptionState *state1, bool operator()(const ExceptionState *state1,
const ExceptionState *state2) const; const ExceptionState *state2) const;
private:
const Network *network_;
}; };
class ExceptionPath; class ExceptionPath;

View File

@ -28,6 +28,8 @@
#include "VertexVisitor.hh" #include "VertexVisitor.hh"
#include "SearchClass.hh" #include "SearchClass.hh"
#include "PowerClass.hh" #include "PowerClass.hh"
#include "ArcDelayCalc.hh"
#include "CircuitSim.hh"
struct Tcl_Interp; struct Tcl_Interp;
@ -114,9 +116,8 @@ public:
// linked network. // linked network.
void readNetlistBefore(); void readNetlistBefore();
// Return true if successful. // Return true if successful.
bool linkDesign(const char *top_cell_name); bool linkDesign(const char *top_cell_name,
bool linkMakeBlackBoxes() const; bool make_black_boxes);
void setLinkMakeBlackBoxes(bool make);
// SDC Swig API. // SDC Swig API.
Instance *currentInstance() const; Instance *currentInstance() const;
@ -773,7 +774,7 @@ public:
const RiseFallBoth *rf, const RiseFallBoth *rf,
float slew); float slew);
void writeSdf(const char *filename, void writeSdf(const char *filename,
Corner *corner, const Corner *corner,
char divider, char divider,
bool include_typ, bool include_typ,
int digits, int digits,
@ -938,7 +939,7 @@ public:
// bug that should be reported. // bug that should be reported.
void updateTiming(bool full); void updateTiming(bool full);
// Invalidate all delay calculations. Arrivals also invalidated. // Invalidate all delay calculations. Arrivals also invalidated.
void delaysInvalid(); void delaysInvalid() const;
// Invalidate all arrival and required times. // Invalidate all arrival and required times.
void arrivalsInvalid(); void arrivalsInvalid();
PinSet startpointPins(); PinSet startpointPins();
@ -1236,7 +1237,7 @@ public:
void setCmdCorner(Corner *corner); void setCmdCorner(Corner *corner);
Corner *findCorner(const char *corner_name); Corner *findCorner(const char *corner_name);
bool multiCorner(); bool multiCorner();
void makeCorners(StringSet *corner_names); virtual void makeCorners(StringSet *corner_names);
// Find all arc delays and vertex slews with delay calculator. // Find all arc delays and vertex slews with delay calculator.
virtual void findDelays(); virtual void findDelays();
// Find arc delays and vertex slews thru to level of to_vertex. // Find arc delays and vertex slews thru to level of to_vertex.
@ -1285,6 +1286,24 @@ public:
const Corner *corner); const Corner *corner);
PwrActivity findClkedActivity(const Pin *pin); 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, void writeTimingModel(const char *lib_name,
const char *cell_name, const char *cell_name,
const char *filename, const char *filename,
@ -1432,7 +1451,6 @@ protected:
ReportPath *report_path_; ReportPath *report_path_;
Power *power_; Power *power_;
Tcl_Interp *tcl_interp_; Tcl_Interp *tcl_interp_;
bool link_make_black_boxes_;
bool update_genclks_; bool update_genclks_;
EquivCells *equiv_cells_; EquivCells *equiv_cells_;
bool graph_sdc_annotated_; bool graph_sdc_annotated_;

View File

@ -39,6 +39,7 @@ class Table1;
typedef Vector<float> FloatSeq; typedef Vector<float> FloatSeq;
typedef Vector<FloatSeq*> FloatTable; typedef Vector<FloatSeq*> FloatTable;
typedef Vector<Table1*> Table1Seq; typedef Vector<Table1*> Table1Seq;
typedef Table1 Waveform;
TableAxisVariable TableAxisVariable
stringTableAxisVariable(const char *variable); stringTableAxisVariable(const char *variable);
@ -66,6 +67,7 @@ public:
// Return values. // Return values.
ArcDelay &gate_delay, ArcDelay &gate_delay,
Slew &drvr_slew) const override; Slew &drvr_slew) const override;
// deprecated 2024-01-07
// related_out_cap arg removed. // related_out_cap arg removed.
void gateDelay(const Pvt *pvt, void gateDelay(const Pvt *pvt,
float in_slew, float in_slew,
@ -455,9 +457,10 @@ public:
// Return values. // Return values.
size_t &index, size_t &index,
bool &exists) const; bool &exists) const;
size_t findAxisClosestIndex(float value) const;
FloatSeq *values() const { return values_; } FloatSeq *values() const { return values_; }
float min() const { return (*values_)[0]; } float min() const;
float max() const { return (*values_)[values_->size() - 1]; } float max() const;
private: private:
TableAxisVariable variable_; TableAxisVariable variable_;
@ -492,26 +495,43 @@ public:
const RiseFall *rf() const { return rf_; } const RiseFall *rf() const { return rf_; }
const TableAxis *slewAxis() const { return slew_axis_.get(); } const TableAxis *slewAxis() const { return slew_axis_.get(); }
const TableAxis *capAxis() const { return cap_axis_.get(); } const TableAxis *capAxis() const { return cap_axis_.get(); }
Table1 voltageWaveform(float in_slew, // Make voltage wavefroms from liberty time/current values.
float load_cap); // Required before voltageTime, timeVoltage, voltageCurrent.
float voltageTime(float in_slew, void makeVoltageWaveforms(float vdd);
float load_cap,
float voltage);
const Table1 *currentWaveform(float slew,
float cap);
float timeCurrent(float slew, float timeCurrent(float slew,
float cap, float cap,
float time); float time);
float timeVoltage(float slew, float timeVoltage(float slew,
float cap, float cap,
float time); float time);
float voltageTime(float in_slew,
float load_cap,
float voltage);
float voltageCurrent(float slew, float voltageCurrent(float slew,
float cap, float cap,
float volt); float volt);
float referenceTime(float slew); 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); 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: private:
void findVoltages(size_t wave_index, void findVoltages(size_t wave_index,
float cap); float cap);
@ -519,27 +539,30 @@ private:
float cap, float cap,
float axis_value, float axis_value,
Table1Seq &waveforms); 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); size_t wave_index);
void waveformMinMaxTime(float slew,
float cap,
Table1Seq &waveforms,
// Return values.
float &min_time,
float &max_time);
// Row. // Row.
TableAxisPtr slew_axis_; TableAxisPtr slew_axis_;
// Column. // Column.
TableAxisPtr cap_axis_; TableAxisPtr cap_axis_;
const RiseFall *rf_; const RiseFall *rf_;
Table1Seq current_waveforms_; Table1Seq current_waveforms_; // from liberty
Table1Seq voltage_waveforms_; Table1Seq voltage_waveforms_;
Table1Seq voltage_currents_; Table1Seq voltage_currents_;
FloatTable voltage_times_;
Table1 *ref_times_; Table1 *ref_times_;
float vdd_; float vdd_;
static constexpr size_t voltage_waveform_step_count_ = 20; static constexpr size_t voltage_waveform_step_count_ = 100;
}; };
class DriverWaveform class DriverWaveform

View File

@ -27,6 +27,8 @@ namespace sta {
class TimingArcAttrs; class TimingArcAttrs;
class WireTimingArc; class WireTimingArc;
class GateTableModel;
class DcalcAnalysisPt;
typedef int TimingArcIndex; typedef int TimingArcIndex;
typedef Vector<TimingArc*> TimingArcSeq; typedef Vector<TimingArc*> TimingArcSeq;
@ -147,7 +149,7 @@ public:
TimingRole *role() const { return role_; }; TimingRole *role() const { return role_; };
TimingSense sense() const; TimingSense sense() const;
// Rise/fall if the arc set is rising_edge or falling_edge. // 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(); } size_t arcCount() const { return arcs_.size(); }
TimingArcSeq &arcs() { return arcs_; } TimingArcSeq &arcs() { return arcs_; }
// Return 1 or 2 arcs matching from transition. // Return 1 or 2 arcs matching from transition.
@ -235,8 +237,11 @@ public:
TimingSense sense() const; TimingSense sense() const;
// Index in TimingArcSet. // Index in TimingArcSet.
unsigned index() const { return index_; } unsigned index() const { return index_; }
TimingModel *model(const OperatingConditions *op_cond) const;
TimingModel *model() const { return model_; } 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; const TimingArc *cornerArc(int ap_index) const;
void setCornerArc(TimingArc *corner_arc, void setCornerArc(TimingArc *corner_arc,
int ap_index); int ap_index);
@ -247,6 +252,7 @@ public:
const TimingArc *arc2); const TimingArc *arc2);
protected: protected:
TimingModel *model(const DcalcAnalysisPt *dcalc_ap) const;
void setIndex(unsigned index); void setIndex(unsigned index);
void addScaledModel(const OperatingConditions *op_cond, void addScaledModel(const OperatingConditions *op_cond,
TimingModel *scaled_model); TimingModel *scaled_model);

View File

@ -97,9 +97,9 @@ public:
RiseFall *asRiseFall() const { return as_rise_fall_; } RiseFall *asRiseFall() const { return as_rise_fall_; }
// Find transition corresponding to string. // Find transition corresponding to string.
static RiseFallBoth *find(const char *tr_str); static RiseFallBoth *find(const char *tr_str);
// for (auto tr : min_max->range()) {} // for (const auto rf : rf->range()) {}
const std::vector<RiseFall*> &range() const { return range_; } const std::vector<RiseFall*> &range() const { return range_; }
// for (auto tr_index : min_max->rangeIndex()) {} // for (const auto rf_index : rf->rangeIndex()) {}
const std::vector<int> &rangeIndex() const { return range_index_; } const std::vector<int> &rangeIndex() const { return range_index_; }
static const int index_count = 3; static const int index_count = 3;

View File

@ -121,10 +121,8 @@ LibertyLibrary::~LibertyLibrary()
delete units_; delete units_;
ocv_derate_map_.deleteContents(); ocv_derate_map_.deleteContents();
for (auto name_volt : supply_voltage_map_) { for (auto [supply_name, volt] : supply_voltage_map_)
const char *supply_name = name_volt.first;
stringDelete(supply_name); stringDelete(supply_name);
}
delete buffers_; delete buffers_;
delete inverters_; delete inverters_;
driver_waveform_map_.deleteContents(); driver_waveform_map_.deleteContents();
@ -204,8 +202,8 @@ BusDclSeq
LibertyLibrary::busDcls() const LibertyLibrary::busDcls() const
{ {
BusDclSeq dcls; BusDclSeq dcls;
for (auto name_dcl : bus_dcls_) for (auto [name, dcl] : bus_dcls_)
dcls.push_back(name_dcl.second); dcls.push_back(dcl);
return dcls; return dcls;
} }
@ -228,10 +226,8 @@ LibertyLibrary::tableTemplates() const
{ {
TableTemplateSeq tbl_templates; TableTemplateSeq tbl_templates;
for (int type = 0; type < table_template_type_count; type++) { for (int type = 0; type < table_template_type_count; type++) {
for (auto name_template : template_maps_[type]) { for (auto [name, tbl_template] : template_maps_[type])
TableTemplate *tbl_template = name_template.second;
tbl_templates.push_back(tbl_template); tbl_templates.push_back(tbl_template);
}
} }
return tbl_templates; return tbl_templates;
} }
@ -950,7 +946,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library,
LibertyCell::~LibertyCell() LibertyCell::~LibertyCell()
{ {
mode_defs_.deleteContents(); mode_defs_.deleteContents();
latch_d_to_q_map_.deleteContents(); latch_enables_.deleteContents();
timing_arc_sets_.deleteContents(); timing_arc_sets_.deleteContents();
port_timing_arc_set_map_.deleteContents(); port_timing_arc_set_map_.deleteContents();
@ -1306,14 +1302,13 @@ LibertyCell::finish(bool infer_latches,
findDefaultCondArcs(); findDefaultCondArcs();
makeLatchEnables(report, debug); makeLatchEnables(report, debug);
if (infer_latches) if (infer_latches)
inferLatchRoles(debug); inferLatchRoles(report, debug);
} }
void void
LibertyCell::findDefaultCondArcs() LibertyCell::findDefaultCondArcs()
{ {
for (auto port_pair_set : port_timing_arc_set_map_) { for (auto [port_pair, sets] : port_timing_arc_set_map_) {
TimingArcSetSeq *sets = port_pair_set.second;
bool has_cond_arcs = false; bool has_cond_arcs = false;
for (auto set : *sets) { for (auto set : *sets) {
if (set->cond()) { if (set->cond()) {
@ -1675,7 +1670,7 @@ class LatchEnable
public: public:
LatchEnable(LibertyPort *data, LatchEnable(LibertyPort *data,
LibertyPort *enable, LibertyPort *enable,
RiseFall *enable_edge, const RiseFall *enable_edge,
FuncExpr *enable_func, FuncExpr *enable_func,
LibertyPort *output, LibertyPort *output,
TimingArcSet *d_to_q, TimingArcSet *d_to_q,
@ -1685,7 +1680,7 @@ public:
LibertyPort *output() const { return output_; } LibertyPort *output() const { return output_; }
LibertyPort *enable() const { return enable_; } LibertyPort *enable() const { return enable_; }
FuncExpr *enableFunc() const { return enable_func_; } 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 *dToQ() const { return d_to_q_; }
TimingArcSet *enToQ() const { return en_to_q_; } TimingArcSet *enToQ() const { return en_to_q_; }
TimingArcSet *setupCheck() const { return setup_check_; } TimingArcSet *setupCheck() const { return setup_check_; }
@ -1693,7 +1688,7 @@ public:
private: private:
LibertyPort *data_; LibertyPort *data_;
LibertyPort *enable_; LibertyPort *enable_;
RiseFall *enable_edge_; const RiseFall *enable_edge_;
FuncExpr *enable_func_; FuncExpr *enable_func_;
LibertyPort *output_; LibertyPort *output_;
TimingArcSet *d_to_q_; TimingArcSet *d_to_q_;
@ -1703,7 +1698,7 @@ private:
LatchEnable::LatchEnable(LibertyPort *data, LatchEnable::LatchEnable(LibertyPort *data,
LibertyPort *enable, LibertyPort *enable,
RiseFall *enable_edge, const RiseFall *enable_edge,
FuncExpr *enable_func, FuncExpr *enable_func,
LibertyPort *output, LibertyPort *output,
TimingArcSet *d_to_q, TimingArcSet *d_to_q,
@ -1736,66 +1731,101 @@ LibertyCell::makeLatchEnables(Report *report,
for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) { for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) {
if (d_to_q->role() == TimingRole::latchDtoQ()) { if (d_to_q->role() == TimingRole::latchDtoQ()) {
LibertyPort *d = d_to_q->from(); LibertyPort *d = d_to_q->from();
for (TimingArcSet *setup_check : timingArcSets(en, d)) { const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
if (setup_check->role() == TimingRole::setup()) { if (en_rf) {
LatchEnable *latch_enable = makeLatchEnable(d, en, q, d_to_q, TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report);
en_to_q, LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q,
setup_check, en_to_q,
debug); setup_check,
for (TimingArc *check_arc : setup_check->arcs()) { debug);
RiseFall *en_rf = latch_enable->enableEdge(); FuncExpr *en_func = latch_enable->enableFunc();
RiseFall *check_rf = check_arc->fromEdge()->asRiseFall(); if (en_func) {
if (check_rf == en_rf) TimingSense en_sense = en_func->portTimingSense(en);
report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.", if (en_sense == TimingSense::positive_unate
library_->name(), && en_rf != RiseFall::rise())
name_, report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.",
en->name(), library_->name(),
q->name(), name_,
en_rf == RiseFall::rise()?"rising":"falling", en->name(),
en->name(), q->name(),
d->name(), en_rf == RiseFall::rise()?"rising":"falling");
check_rf==RiseFall::rise()?"rising":"falling"); else if (en_sense == TimingSense::negative_unate
FuncExpr *en_func = latch_enable->enableFunc(); && en_rf != RiseFall::fall())
if (en_func) { report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.",
TimingSense en_sense = en_func->portTimingSense(en); library_->name(),
if (en_sense == TimingSense::positive_unate name_,
&& en_rf != RiseFall::rise()) en->name(),
report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.", q->name(),
library_->name(), en_rf == RiseFall::rise()?"rising":"falling");
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 * FuncExpr *
LibertyCell::findLatchEnableFunc(LibertyPort *data, LibertyCell::findLatchEnableFunc(const LibertyPort *d,
LibertyPort *enable) const const LibertyPort *en,
const RiseFall *en_rf) const
{ {
for (auto seq : sequentials_) { for (auto seq : sequentials_) {
if (seq->isLatch() if (seq->isLatch()
&& seq->data() && seq->data()
&& seq->data()->hasPort(data) && seq->data()->hasPort(d)
&& seq->clock() && seq->clock()
&& seq->clock()->hasPort(enable)) && seq->clock()->hasPort(en)) {
return seq->clock(); 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; return nullptr;
} }
@ -1803,29 +1833,36 @@ LibertyCell::findLatchEnableFunc(LibertyPort *data,
LatchEnable * LatchEnable *
LibertyCell::makeLatchEnable(LibertyPort *d, LibertyCell::makeLatchEnable(LibertyPort *d,
LibertyPort *en, LibertyPort *en,
const RiseFall *en_rf,
LibertyPort *q, LibertyPort *q,
TimingArcSet *d_to_q, TimingArcSet *d_to_q,
TimingArcSet *en_to_q, TimingArcSet *en_to_q,
TimingArcSet *setup_check, TimingArcSet *setup_check,
Debug *debug) Debug *debug)
{ {
RiseFall *en_rf = en_to_q->isRisingFallingEdge(); FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf);
FuncExpr *en_func = findLatchEnableFunc(d, en);
LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q, LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q,
d_to_q, en_to_q, setup_check); d_to_q, en_to_q, setup_check);
// Multiple enables for D->Q pairs are not supported. latch_enables_.push_back(latch_enable);
if (latch_d_to_q_map_[d_to_q])
delete latch_d_to_q_map_[d_to_q];
latch_d_to_q_map_[d_to_q] = latch_enable; latch_d_to_q_map_[d_to_q] = latch_enable;
latch_check_map_[setup_check] = latch_enable; latch_check_map_[setup_check] = latch_enable;
latch_data_ports_.insert(d); latch_data_ports_.insert(d);
debugPrint(debug, "liberty", 2, "latch d=%s en=%s q=%s", debugPrint(debug, "liberty_latch", 1,
d->name(), en->name(), q->name()); "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; return latch_enable;
} }
void void
LibertyCell::inferLatchRoles(Debug *debug) LibertyCell::inferLatchRoles(Report *report,
Debug *debug)
{ {
if (hasInferedRegTimingArcs()) { if (hasInferedRegTimingArcs()) {
// Hunt down potential latch D/EN/Q triples. // Hunt down potential latch D/EN/Q triples.
@ -1845,15 +1882,14 @@ LibertyCell::inferLatchRoles(Debug *debug)
// Previously identified as D->Q arc. // Previously identified as D->Q arc.
|| d_to_q_role == TimingRole::latchDtoQ()) { || d_to_q_role == TimingRole::latchDtoQ()) {
LibertyPort *d = d_to_q->from(); LibertyPort *d = d_to_q->from();
// Check for setup check from en -> d. const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
for (TimingArcSet *setup_check : timingArcSets(en, d)) { if (en_rf) {
if (setup_check->role() == TimingRole::setup()) { TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report);
makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug); makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q, setup_check, debug);
d_to_q->setRole(TimingRole::latchDtoQ()); d_to_q->setRole(TimingRole::latchDtoQ());
en_to_q->setRole(TimingRole::latchEnToQ()); en_to_q->setRole(TimingRole::latchEnToQ());
} }
} }
}
} }
} }
} }
@ -1867,22 +1903,26 @@ LibertyCell::isLatchData(LibertyPort *port)
} }
void void
LibertyCell::latchEnable(TimingArcSet *d_to_q_set, LibertyCell::latchEnable(const TimingArcSet *d_to_q_set,
// Return values. // Return values.
LibertyPort *&enable_port, const LibertyPort *&enable_port,
FuncExpr *&enable_func, const FuncExpr *&enable_func,
RiseFall *&enable_edge) const const RiseFall *&enable_edge) const
{ {
enable_port = nullptr;
LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set); LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set);
if (latch_enable) { if (latch_enable) {
enable_port = latch_enable->enable(); enable_port = latch_enable->enable();
enable_func = latch_enable->enableFunc(); enable_func = latch_enable->enableFunc();
enable_edge = latch_enable->enableEdge(); enable_edge = latch_enable->enableEdge();
} }
else {
enable_port = nullptr;
enable_func = nullptr;
enable_edge = nullptr;
}
} }
RiseFall * const RiseFall *
LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set) LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set)
{ {
LatchEnable *latch_enable = latch_check_map_.findKey(check_set); LatchEnable *latch_enable = latch_check_map_.findKey(check_set);
@ -1893,7 +1933,7 @@ LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set)
} }
void void
LibertyCell::ensureVoltageWaveforms() LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPt *dcalc_ap)
{ {
if (!have_voltage_waveforms_) { if (!have_voltage_waveforms_) {
float vdd = 0.0; // shutup gcc float vdd = 0.0; // shutup gcc
@ -1903,7 +1943,7 @@ LibertyCell::ensureVoltageWaveforms()
criticalError(1120, "library missing vdd"); criticalError(1120, "library missing vdd");
for (TimingArcSet *arc_set : timingArcSets()) { for (TimingArcSet *arc_set : timingArcSets()) {
for (TimingArc *arc : arc_set->arcs()) { for (TimingArc *arc : arc_set->arcs()) {
GateTableModel*model = dynamic_cast<GateTableModel*>(arc->model()); GateTableModel *model = arc->gateTableModel(dcalc_ap);
if (model) { if (model) {
OutputWaveforms *output_waveforms = model->outputWaveforms(); OutputWaveforms *output_waveforms = model->outputWaveforms();
if (output_waveforms) if (output_waveforms)

542
liberty/Liberty.i Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
%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<Cell*>(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

View File

@ -695,22 +695,22 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group)
} }
bool missing_threshold = false; bool missing_threshold = false;
for (auto tr : RiseFall::range()) { for (auto rf : RiseFall::range()) {
int tr_index = tr->index(); int rf_index = rf->index();
if (!have_input_threshold_[tr_index]) { if (!have_input_threshold_[rf_index]) {
libWarn(1145, group, "input_threshold_pct_%s not found.", tr->name()); libWarn(1145, group, "input_threshold_pct_%s not found.", rf->name());
missing_threshold = true; missing_threshold = true;
} }
if (!have_output_threshold_[tr_index]) { if (!have_output_threshold_[rf_index]) {
libWarn(1146, group, "output_threshold_pct_%s not found.", tr->name()); libWarn(1146, group, "output_threshold_pct_%s not found.", rf->name());
missing_threshold = true; missing_threshold = true;
} }
if (!have_slew_lower_threshold_[tr_index]) { if (!have_slew_lower_threshold_[rf_index]) {
libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", tr->name()); libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", rf->name());
missing_threshold = true; missing_threshold = true;
} }
if (!have_slew_upper_threshold_[tr_index]) { if (!have_slew_upper_threshold_[rf_index]) {
libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", tr->name()); libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", rf->name());
missing_threshold = true; missing_threshold = true;
} }
} }
@ -3927,8 +3927,8 @@ LibertyReader::endTiming(LibertyGroup *group)
{ {
if (timing_) { if (timing_) {
// Set scale factor type in constraint tables. // Set scale factor type in constraint tables.
for (auto tr : RiseFall::range()) { for (auto rf : RiseFall::range()) {
TableModel *model = timing_->constraint(tr); TableModel *model = timing_->constraint(rf);
if (model) { if (model) {
ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType()); ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType());
model->setScaleFactorType(type); model->setScaleFactorType(type);
@ -5110,13 +5110,13 @@ LibertyReader::endOcvDerateFactors(LibertyGroup *)
{ {
if (ocv_derate_) { if (ocv_derate_) {
for (auto early_late : derate_type_->range()) { 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) { if (path_type_ == PathType::clk_and_data) {
ocv_derate_->setDerateTable(tr, early_late, PathType::clk, table_); ocv_derate_->setDerateTable(rf, early_late, PathType::clk, table_);
ocv_derate_->setDerateTable(tr, early_late, PathType::data, table_); ocv_derate_->setDerateTable(rf, early_late, PathType::data, table_);
} }
else else
ocv_derate_->setDerateTable(tr, early_late, path_type_, table_); ocv_derate_->setDerateTable(rf, early_late, path_type_, table_);
} }
} }
} }

View File

@ -217,11 +217,11 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
if (axis3) if (axis3)
fprintf(stream_, " variable_3 : %s;\n", fprintf(stream_, " variable_3 : %s;\n",
tableVariableString(axis3->variable())); tableVariableString(axis3->variable()));
if (axis1) if (axis1 && axis1->values())
writeTableAxis4(axis1, 1); writeTableAxis4(axis1, 1);
if (axis2) if (axis2 && axis2->values())
writeTableAxis4(axis2, 2); writeTableAxis4(axis2, 2);
if (axis3) if (axis3 && axis3->values())
writeTableAxis4(axis3, 3); writeTableAxis4(axis3, 3);
fprintf(stream_, " }\n"); fprintf(stream_, " }\n");
} }

View File

@ -31,6 +31,9 @@ using std::max;
using std::abs; using std::abs;
using std::make_shared; using std::make_shared;
size_t
findValueIndex(float value,
const FloatSeq *values);
static void static void
deleteSigmaModels(TableModel *models[EarlyLate::index_count]); deleteSigmaModels(TableModel *models[EarlyLate::index_count]);
static string static string
@ -1425,6 +1428,25 @@ TableAxis::~TableAxis()
delete values_; 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 bool
TableAxis::inBounds(float value) const TableAxis::inBounds(float value) const
{ {
@ -1434,14 +1456,22 @@ TableAxis::inBounds(float value) const
&& value <= (*values_)[size - 1]; && value <= (*values_)[size - 1];
} }
// Bisection search.
size_t size_t
TableAxis::findAxisIndex(float value) const TableAxis::findAxisIndex(float value) const
{ {
size_t size = values_->size(); return findValueIndex(value, values_);
if (size <= 1 || value <= (*values_)[0]) }
// 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; 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 max_index-1 for value too large so interpolation pts are index,index+1.
return size - 2; return size - 2;
else { else {
@ -1449,7 +1479,7 @@ TableAxis::findAxisIndex(float value) const
int upper = size; int upper = size;
while (upper - lower > 1) { while (upper - lower > 1) {
int mid = (upper + lower) >> 1; int mid = (upper + lower) >> 1;
if (value >= (*values_)[mid]) if (value >= (*values)[mid])
lower = mid; lower = mid;
else else
upper = mid; upper = mid;
@ -1486,6 +1516,31 @@ TableAxis::findAxisIndex(float value,
exists = false; 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 * const char *
TableAxis::variableString() const TableAxis::variableString() const
{ {
@ -1588,7 +1643,6 @@ OutputWaveforms::~OutputWaveforms()
current_waveforms_.deleteContents(); current_waveforms_.deleteContents();
voltage_waveforms_.deleteContents(); voltage_waveforms_.deleteContents();
voltage_currents_.deleteContents(); voltage_currents_.deleteContents();
voltage_times_.deleteContents();
delete ref_times_; delete ref_times_;
} }
@ -1616,10 +1670,9 @@ OutputWaveforms::makeVoltageWaveforms(float vdd)
size_t size = current_waveforms_.size(); size_t size = current_waveforms_.size();
voltage_waveforms_.resize(size); voltage_waveforms_.resize(size);
voltage_currents_.resize(size); voltage_currents_.resize(size);
voltage_times_.resize(size);
size_t cap_count = cap_axis_->size(); size_t cap_count = cap_axis_->size();
for (size_t slew_index = 0; slew_index < slew_axis_->size(); slew_index++) { 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; size_t wave_index = slew_index * cap_count + cap_index;
findVoltages(wave_index, cap_axis_->axisValue(cap_index)); findVoltages(wave_index, cap_axis_->axisValue(cap_index));
} }
@ -1650,6 +1703,7 @@ OutputWaveforms::findVoltages(size_t wave_index,
prev_time = time; prev_time = time;
prev_current = current; prev_current = current;
} }
(*volts)[volts->size() - 1] = vdd_;
Table1 *volt_table = new Table1(volts, currents->axis1ptr()); Table1 *volt_table = new Table1(volts, currents->axis1ptr());
voltage_waveforms_[wave_index] = volt_table; voltage_waveforms_[wave_index] = volt_table;
@ -1660,39 +1714,33 @@ OutputWaveforms::findVoltages(size_t wave_index,
FloatSeq *currents1 = new FloatSeq(*currents->values()); FloatSeq *currents1 = new FloatSeq(*currents->values());
Table1 *volt_currents = new Table1(currents1, volt_axis); Table1 *volt_currents = new Table1(currents1, volt_axis);
voltage_currents_[wave_index] = volt_currents; 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, OutputWaveforms::currentWaveform(float slew,
float cap) float cap)
{ {
size_t slew_index = slew_axis_->findAxisIndex(slew); FloatSeq *times = new FloatSeq;
size_t cap_index = cap_axis_->findAxisIndex(cap); FloatSeq *currents = new FloatSeq;
size_t wave_index = slew_index * cap_axis_->size() + cap_index; 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<TableAxis>(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]; return current_waveforms_[wave_index];
} }
@ -1701,7 +1749,9 @@ OutputWaveforms::timeCurrent(float slew,
float cap, float cap,
float time) 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 float
@ -1709,7 +1759,81 @@ OutputWaveforms::timeVoltage(float slew,
float cap, float cap,
float time) 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 float
@ -1773,14 +1897,11 @@ Table1
OutputWaveforms::voltageWaveform(float slew, OutputWaveforms::voltageWaveform(float slew,
float cap) 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 *times = new FloatSeq;
FloatSeq *volts = new FloatSeq; FloatSeq *volts = new FloatSeq;
for (size_t i = 0; i < voltage_waveform_step_count_; i++) { for (size_t i = 0; i <= voltage_waveform_step_count_; i++) {
float time = min_time + i * time_step; float volt = i * vdd_ / voltage_waveform_step_count_;
float volt = timeVoltage(slew, cap, time); float time = voltageTime(slew, cap, volt);
times->push_back(time); times->push_back(time);
volts->push_back(volt); volts->push_back(volt);
} }
@ -1788,42 +1909,21 @@ OutputWaveforms::voltageWaveform(float slew,
return Table1(volts, time_axis); return Table1(volts, time_axis);
} }
void const Table1 *
OutputWaveforms::waveformMinMaxTime(float slew, OutputWaveforms::voltageWaveformRaw(float slew,
float cap, float cap)
Table1Seq &waveforms,
// Return values.
float &min_time,
float &max_time)
{ {
size_t slew_index = slew_axis_->findAxisIndex(slew); size_t slew_index = slew_axis_->findAxisClosestIndex(slew);
size_t cap_index = cap_axis_->findAxisIndex(cap); size_t cap_index = cap_axis_->findAxisClosestIndex(cap);
size_t cap_count = cap_axis_->size(); size_t cap_count = cap_axis_->size();
size_t wave_index00 = slew_index * cap_count + cap_index; size_t wave_index = slew_index * cap_count + cap_index;
size_t wave_index01 = slew_index * cap_count + (cap_index + 1); return voltage_waveforms_[wave_index];
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());
} }
float float
OutputWaveforms::voltageTime(float slew, OutputWaveforms::voltageTime(float slew,
float cap, float cap,
float volt) float volt)
{ {
size_t slew_index = slew_axis_->findAxisIndex(slew); size_t slew_index = slew_axis_->findAxisIndex(slew);
size_t cap_index = cap_axis_->findAxisIndex(cap); size_t cap_index = cap_axis_->findAxisIndex(cap);
@ -1845,33 +1945,109 @@ OutputWaveforms::voltageTime(float slew,
double x2u = cap_axis_->axisValue(index2 + 1); double x2u = cap_axis_->axisValue(index2 + 1);
double dx2 = (x2 - x2l) / (x2u - x2l); double dx2 = (x2 - x2l) / (x2u - x2l);
double y00 = voltageTime1(volt, wave_index00); double time = voltageTime1(volt, dx1, dx2, wave_index00, wave_index01,
double y01 = voltageTime1(volt, wave_index01); wave_index10, wave_index11);
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;
return time; return time;
} }
float float
OutputWaveforms::voltageTime1(float voltage, OutputWaveforms::beginTime(float slew,
size_t wave_index) float cap)
{ {
FloatSeq *voltage_times = voltage_times_[wave_index]; return beginEndTime(slew, cap, true);
float volt_step = vdd_ / voltage_waveform_step_count_; }
size_t volt_idx = voltage / volt_step;
if (volt_idx >= voltage_times->size() - 1) float
return (*voltage_times)[voltage_times->size() - 1]; OutputWaveforms::endTime(float slew,
else { float cap)
double time0 = (*voltage_times)[volt_idx]; {
double time1 = (*voltage_times)[volt_idx + 1]; return beginEndTime(slew, cap, false);
double time = time0 + (time1 - time0) * (voltage - volt_step * volt_idx); }
return time;
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<TableAxis>(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]);
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////

View File

@ -21,6 +21,8 @@
#include "TimingRole.hh" #include "TimingRole.hh"
#include "Liberty.hh" #include "Liberty.hh"
#include "TimingArc.hh" #include "TimingArc.hh"
#include "DcalcAnalysisPt.hh"
#include "TableModel.hh"
namespace sta { namespace sta {
@ -316,13 +318,13 @@ TimingArcSet::sense() const
return attrs_->timingSense(); return attrs_->timingSense();
} }
RiseFall * const RiseFall *
TimingArcSet::isRisingFallingEdge() const TimingArcSet::isRisingFallingEdge() const
{ {
int arc_count = arcs_.size(); int arc_count = arcs_.size();
if (arc_count == 2) { if (arc_count == 2) {
RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall(); const RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall();
RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall(); const RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall();
if (from_rf1 == from_rf2) if (from_rf1 == from_rf2)
return from_rf1; return from_rf1;
} }
@ -545,18 +547,42 @@ TimingArc::~TimingArc()
delete scaled_models_; delete scaled_models_;
} }
TimingModel * GateTimingModel *
TimingArc::model(const OperatingConditions *op_cond) const TimingArc::gateModel(const DcalcAnalysisPt *dcalc_ap) const
{ {
if (scaled_models_) { return dynamic_cast<GateTimingModel*>(model(dcalc_ap));
TimingModel *model = scaled_models_->findKey(op_cond); }
if (model)
return model; GateTableModel *
else TimingArc::gateTableModel() const
return model_; {
return dynamic_cast<GateTableModel*>(model_);
}
GateTableModel *
TimingArc::gateTableModel(const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<GateTableModel*>(model(dcalc_ap));
}
CheckTimingModel *
TimingArc::checkModel(const DcalcAnalysisPt *dcalc_ap) const
{
return dynamic_cast<CheckTimingModel*>(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 corner_arc->model();
return model_;
} }
void void

View File

@ -320,8 +320,9 @@ public:
int from() const { return from_; } int from() const { return from_; }
int to() const { return to_; } int to() const { return to_; }
ConcretePortSeq &members() { return members_; } ConcretePortSeq &members() { return members_; }
const ConcretePortSeq &members() const { return members_; }
void setDirection(PortDirection *direction); void setDirection(PortDirection *direction);
PortDirection *direction() { return direction_; } PortDirection *direction() const { return direction_; }
private: private:
int from_; int from_;
@ -385,9 +386,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left,
} }
// Make the bus ports. // Make the bus ports.
for (auto name_bus : bus_map) { for (const auto& [bus_name, bus_port] : bus_map) {
string bus_name = name_bus.first;
BusPort &bus_port = name_bus.second;
int from = bus_port.from(); int from = bus_port.from();
int to = bus_port.to(); int to = bus_port.to();
size_t size = to - from + 1; size_t size = to - from + 1;

View File

@ -18,11 +18,13 @@
namespace eval sta { 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 { proc_redirect link_design {
variable current_design_name variable current_design_name
parse_key_args "link_design" args keys {} flags {-no_black_boxes}
check_argc_eq0or1 "link_design" $args check_argc_eq0or1 "link_design" $args
if { $args == "" } { if { $args == "" } {
set top_cell_name "" set top_cell_name ""
@ -37,7 +39,11 @@ proc_redirect link_design {
set top_cell_name $current_design_name 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 # sta namespace end

818
network/Network.i Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
%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

View File

@ -26,9 +26,11 @@ proc_redirect report_instance {
check_argc_eq1 "report_instance" $args check_argc_eq1 "report_instance" $args
if { [info exists flags(-connections)] } { if { [info exists flags(-connections)] } {
# deprecated 2024-01-17
sta_warn 233 "report_instance -connections is deprecated." sta_warn 233 "report_instance -connections is deprecated."
} }
if { [info exists flags(-verbose)] } { if { [info exists flags(-verbose)] } {
# deprecated 2024-01-17
sta_warn 234 "report_instance -verbose is deprecated." sta_warn 234 "report_instance -verbose is deprecated."
} }
set instance_path [lindex $args 0] set instance_path [lindex $args 0]
@ -183,12 +185,15 @@ proc_redirect report_net {
check_argc_eq1 "report_net" $args check_argc_eq1 "report_net" $args
if { [info exists flags(-connections)] } { if { [info exists flags(-connections)] } {
# deprecated 2024-01-17
sta_warn 235 "report_net -connections is deprecated." sta_warn 235 "report_net -connections is deprecated."
} }
if { [info exists flags(-verbose)] } { if { [info exists flags(-verbose)] } {
# deprecated 2024-01-17
sta_warn 236 "report_net -verbose is deprecated." sta_warn 236 "report_net -verbose is deprecated."
} }
if { [info exists flags(-hier_pins)] } { if { [info exists flags(-hier_pins)] } {
# deprecated 2024-01-17
sta_warn 237 "report_net -hier_pins is deprecated." sta_warn 237 "report_net -hier_pins is deprecated."
} }

View File

@ -1,7 +1,3 @@
%module NetworkEdit
%{
// OpenSTA, Static Timing Analyzer // OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc. // Copyright (c) 2024, Parallax Software, Inc.
// //
@ -18,23 +14,23 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
%module NetworkEdit
%{
using sta::Cell; using sta::Cell;
using sta::Instance; using sta::Instance;
using sta::Net; using sta::Net;
using sta::Port; using sta::Port;
using sta::Pin; using sta::Pin;
using sta::NetworkEdit; using sta::NetworkEdit;
using sta::cmdEditNetwork;
namespace sta {
NetworkEdit *
cmdEditNetwork();
}
%} %}
////////////////////////////////////////////////////////////////
//
// SWIG type definitions.
//
////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// //
// C++ functions visible as TCL functions. // C++ functions visible as TCL functions.
@ -68,7 +64,7 @@ Net *
make_net_cmd(const char *name, make_net_cmd(const char *name,
Instance *parent) Instance *parent)
{ {
Net *net = cmdEditNetwork()->makeNet(name, parent); Net *net = sta::cmdEditNetwork()->makeNet(name, parent);
// Sta notification unnecessary. // Sta notification unnecessary.
return net; return net;
} }

View File

@ -133,38 +133,6 @@ proc parse_connect_pin { arg } {
return [list $inst $port] 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} define_cmd_args "disconnect_pin" {net -all|pin}

View File

@ -246,10 +246,8 @@ ConcretePiElmore::unannotatedLoads(const Pin *drvr_pin,
const Parasitics *parasitics) const const Parasitics *parasitics) const
{ {
PinSet loads = parasitics->loads(drvr_pin); PinSet loads = parasitics->loads(drvr_pin);
for (auto pin_elmore : loads_) { for (const auto [load, elmore] : loads_)
const Pin *load = pin_elmore.first;
loads.erase(load); loads.erase(load);
}
return loads; return loads;
} }
@ -371,10 +369,8 @@ ConcretePiPoleResidue::unannotatedLoads(const Pin *drvr_pin,
const Parasitics *parasitics) const const Parasitics *parasitics) const
{ {
PinSet loads = parasitics->loads(drvr_pin); PinSet loads = parasitics->loads(drvr_pin);
for (auto pin_pole_residue : load_pole_residue_) { for (const auto& [load, pole_residue] : load_pole_residue_)
const Pin *load = pin_pole_residue.first;
loads.erase(load); loads.erase(load);
}
return loads; return loads;
} }
@ -395,6 +391,7 @@ ConcreteParasiticNode::ConcreteParasiticNode(const Pin *pin,
bool is_external) : bool is_external) :
is_net_(false), is_net_(false),
is_external_(is_external), is_external_(is_external),
id_(0),
cap_(0.0) cap_(0.0)
{ {
net_pin_.pin_ = pin; net_pin_.pin_ = pin;
@ -506,14 +503,10 @@ ConcreteParasiticNetwork::~ConcreteParasiticNetwork()
void void
ConcreteParasiticNetwork::deleteNodes() ConcreteParasiticNetwork::deleteNodes()
{ {
for (auto id_node : sub_nodes_) { for (const auto& [id, node] : sub_nodes_)
ConcreteParasiticNode *node = id_node.second;
delete node; delete node;
} for (const auto& [pin, node] : pin_nodes_)
for (auto pin_node : pin_nodes_) {
ConcreteParasiticNode *node = pin_node.second;
delete node; delete node;
}
} }
void void
@ -547,14 +540,10 @@ ParasiticNodeSeq
ConcreteParasiticNetwork::nodes() const ConcreteParasiticNetwork::nodes() const
{ {
ParasiticNodeSeq nodes; ParasiticNodeSeq nodes;
for (auto pin_node : pin_nodes_) { for (const auto [pin, node] : pin_nodes_)
ParasiticNode *node = pin_node.second;
nodes.push_back(node); nodes.push_back(node);
} for (const auto& [id, node] : sub_nodes_)
for (auto id_node : sub_nodes_) {
ParasiticNode *node = id_node.second;
nodes.push_back(node); nodes.push_back(node);
}
return nodes; return nodes;
} }
@ -562,14 +551,12 @@ float
ConcreteParasiticNetwork::capacitance() const ConcreteParasiticNetwork::capacitance() const
{ {
float cap = 0.0; float cap = 0.0;
for (auto id_node : sub_nodes_) { for (const auto& [id, node] : sub_nodes_) {
ConcreteParasiticNode *node = id_node.second;
if (!node->isExternal()) if (!node->isExternal())
cap += node->capacitance(); cap += node->capacitance();
} }
for (auto pin_node : pin_nodes_) { for (const auto [pin, node] : pin_nodes_) {
ConcreteParasiticNode *node = pin_node.second;
if (!node->isExternal()) if (!node->isExternal())
cap += node->capacitance(); cap += node->capacitance();
} }
@ -617,7 +604,8 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net,
if (id_node == sub_nodes_.end()) { if (id_node == sub_nodes_.end()) {
node = new ConcreteParasiticNode(net, id, net != net_); node = new ConcreteParasiticNode(net, id, net != net_);
sub_nodes_[net_id] = node; 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 else
node = id_node->second; node = id_node->second;
@ -790,8 +778,7 @@ ConcreteParasitics::deleteParasitics()
{ {
int ap_count = corners_->parasiticAnalysisPtCount(); int ap_count = corners_->parasiticAnalysisPtCount();
int ap_rf_count = ap_count * RiseFall::index_count; int ap_rf_count = ap_count * RiseFall::index_count;
for (auto drvr_parasitics : drvr_parasitic_map_) { for (const auto [drvr, parasitics] : drvr_parasitic_map_) {
ConcreteParasitic **parasitics = drvr_parasitics.second;
if (parasitics) { if (parasitics) {
for (int i = 0; i < ap_rf_count; i++) for (int i = 0; i < ap_rf_count; i++)
delete parasitics[i]; delete parasitics[i];
@ -800,8 +787,7 @@ ConcreteParasitics::deleteParasitics()
} }
drvr_parasitic_map_.clear(); drvr_parasitic_map_.clear();
for (auto net_parasitics : parasitic_network_map_) { for (const auto [net, parasitics] : parasitic_network_map_) {
ConcreteParasiticNetwork **parasitics = net_parasitics.second;
if (parasitics) { if (parasitics) {
for (int i = 0; i < ap_count; i++) for (int i = 0; i < ap_count; i++)
delete parasitics[i]; delete parasitics[i];
@ -817,8 +803,8 @@ ConcreteParasitics::deleteParasitics(const Pin *drvr_pin,
{ {
ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin]; ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin];
if (parasitics) { if (parasitics) {
for (auto tr : RiseFall::range()) { for (auto rf : RiseFall::range()) {
int ap_rf_index = parasiticAnalysisPtIndex(ap, tr); int ap_rf_index = parasiticAnalysisPtIndex(ap, rf);
delete parasitics[ap_rf_index]; delete parasitics[ap_rf_index];
parasitics[ap_rf_index] = nullptr; parasitics[ap_rf_index] = nullptr;
} }
@ -1247,8 +1233,10 @@ ConcreteParasitics::makeParasiticNetwork(const Net *net,
ConcreteParasiticNetwork *parasitic = parasitics[ap_index]; ConcreteParasiticNetwork *parasitic = parasitics[ap_index];
if (parasitic) { if (parasitic) {
delete parasitic; delete parasitic;
for (const Pin *drvr_pin : *network_->drivers(net)) if (net) {
deleteParasitics(drvr_pin, ap); for (const Pin *drvr_pin : *network_->drivers(net))
deleteParasitics(drvr_pin, ap);
}
} }
parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_); parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_);
parasitics[ap_index] = parasitic; parasitics[ap_index] = parasitic;
@ -1446,6 +1434,14 @@ ConcreteParasitics::net(const ParasiticNode *node,
return cnode->net(network); return cnode->net(network);
} }
unsigned
ConcreteParasitics::netId(const ParasiticNode *node) const
{
const ConcreteParasiticNode *cnode =
static_cast<const ConcreteParasiticNode*>(node);
return cnode->id();
}
bool bool
ConcreteParasitics::isExternal(const ParasiticNode *node) const ConcreteParasitics::isExternal(const ParasiticNode *node) const
{ {

View File

@ -133,6 +133,7 @@ public:
const Pin *pin(const ParasiticNode *node) const override; const Pin *pin(const ParasiticNode *node) const override;
const Net *net(const ParasiticNode *node, const Net *net(const ParasiticNode *node,
const Network *network) const override; const Network *network) const override;
unsigned netId(const ParasiticNode *node) const override;
bool isExternal(const ParasiticNode *node) const override; bool isExternal(const ParasiticNode *node) const override;
float nodeGndCap(const ParasiticNode *node) const override; float nodeGndCap(const ParasiticNode *node) const override;

View File

@ -43,7 +43,7 @@ typedef std::map<const Pin*, float> ConcreteElmoreLoadMap;
typedef std::map<const Pin*, ConcretePoleResidue> ConcretePoleResidueMap; typedef std::map<const Pin*, ConcretePoleResidue> ConcretePoleResidueMap;
typedef std::map<NetIdPair,ConcreteParasiticNode*, typedef std::map<NetIdPair,ConcreteParasiticNode*,
NetIdPairLess> ConcreteParasiticSubNodeMap; NetIdPairLess> ConcreteParasiticSubNodeMap;
typedef std::map<const Pin*, ConcreteParasiticNode*, PinIdLess> ConcreteParasiticPinNodeMap; typedef std::map<const Pin*,ConcreteParasiticNode*,PinIdLess> ConcreteParasiticPinNodeMap;
typedef std::set<ParasiticNode*> ParasiticNodeSet; typedef std::set<ParasiticNode*> ParasiticNodeSet;
typedef std::set<ParasiticResistor*> ParasiticResistorSet; typedef std::set<ParasiticResistor*> ParasiticResistorSet;
typedef std::vector<ParasiticResistor*> ParasiticResistorSeq; typedef std::vector<ParasiticResistor*> ParasiticResistorSeq;
@ -262,6 +262,7 @@ public:
float capacitance() const { return cap_; } float capacitance() const { return cap_; }
const char *name(const Network *network) const; const char *name(const Network *network) const;
const Net *net(const Network *network) const; const Net *net(const Network *network) const;
unsigned id() const { return id_; }
bool isExternal() const { return is_external_; } bool isExternal() const { return is_external_; }
const Pin *pin() const; const Pin *pin() const;
void incrCapacitance(float cap); void incrCapacitance(float cap);

View File

@ -18,6 +18,7 @@
#include "Wireload.hh" #include "Wireload.hh"
#include "Liberty.hh" #include "Liberty.hh"
#include "PortDirection.hh"
#include "Network.hh" #include "Network.hh"
#include "Sdc.hh" #include "Sdc.hh"
#include "Parasitics.hh" #include "Parasitics.hh"
@ -147,7 +148,7 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
bool &elmore_use_load_cap) bool &elmore_use_load_cap)
{ {
if (wireload_res == 0.0 if (wireload_res == 0.0
|| fanout == 0) { || fanout == 0.0) {
// No resistance, so load is capacitance only. // No resistance, so load is capacitance only.
c2 = wireload_cap + net_pin_cap; c2 = wireload_cap + net_pin_cap;
rpi = 0.0; rpi = 0.0;
@ -168,22 +169,19 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
network_->connectedPinIterator(drvr_pin); network_->connectedPinIterator(drvr_pin);
while (load_iter->hasNext()) { while (load_iter->hasNext()) {
const Pin *load_pin = load_iter->next(); const Pin *load_pin = load_iter->next();
Port *port = network_->port(load_pin);
double cap = 0.0;
// Bidirects don't count themselves as loads. // Bidirects don't count themselves as loads.
if (load_pin != drvr_pin && network_->isLoad(load_pin)) { if (load_pin == drvr_pin)
Port *port = network_->port(load_pin); cap = sdc_->portExtCap(port, rf, corner, min_max);
double load_cap = 0.0; else if (network_->isLeaf(load_pin))
if (network_->isLeaf(load_pin)) cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max) + cap_fanout;
load_cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max); else if (network_->isTopLevelPort(load_pin))
else if (network_->isTopLevelPort(load_pin)) cap = sdc_->portExtCap(port, rf, corner, min_max) + cap_fanout;
load_cap = sdc_->portExtCap(port, rf, corner, min_max); double y2_ = res_fanout * cap * cap;
else y1 += cap;
report_->critical(1050, "load pin not leaf or top level"); y2 += -y2_;
double cap = load_cap + cap_fanout; y3 += y2_ * res_fanout * cap;
double y2_ = res_fanout * cap * cap;
y1 += cap;
y2 += -y2_;
y3 += y2_ * res_fanout * cap;
}
} }
delete load_iter; delete load_iter;
@ -196,6 +194,8 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
else { else {
c1 = static_cast<float>(y2 * y2 / y3); c1 = static_cast<float>(y2 * y2 / y3);
c2 = static_cast<float>(y1 - y2 * y2 / y3); c2 = static_cast<float>(y1 - y2 * y2 / y3);
if (c2 < 0.0)
c2 = 0.0;
rpi = static_cast<float>(-y3 * y3 / (y2 * y2 * y2)); rpi = static_cast<float>(-y3 * y3 / (y2 * y2 * y2));
} }
elmore_res = static_cast<float>(res_fanout); elmore_res = static_cast<float>(res_fanout);

View File

@ -203,29 +203,32 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin,
const MinMax *min_max, const MinMax *min_max,
const ParasiticAnalysisPt *ap) const ParasiticAnalysisPt *ap)
{ {
Parasitic *parasitic = nullptr;
const Net *net = findParasiticNet(drvr_pin); const Net *net = findParasiticNet(drvr_pin);
Parasitic *parasitic = makeParasiticNetwork(net, false, ap); if (net) {
const OperatingConditions *op_cond = sdc_->operatingConditions(min_max); parasitic = makeParasiticNetwork(net, false, ap);
float wireload_cap, wireload_res; const OperatingConditions *op_cond = sdc_->operatingConditions(min_max);
wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res); float wireload_cap, wireload_res;
wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res);
WireloadTree tree = WireloadTree::balanced; WireloadTree tree = WireloadTree::balanced;
if (op_cond) if (op_cond)
tree = op_cond->wireloadTree(); tree = op_cond->wireloadTree();
switch (tree) { switch (tree) {
case WireloadTree::worst_case: case WireloadTree::worst_case:
makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap, makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap,
wireload_res, fanout); wireload_res, fanout);
break; break;
case WireloadTree::balanced: case WireloadTree::balanced:
makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap,
wireload_res, fanout); wireload_res, fanout);
break; break;
case WireloadTree::best_case: case WireloadTree::best_case:
case WireloadTree::unknown: case WireloadTree::unknown:
makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap,
wireload_res, fanout); wireload_res, fanout);
break; break;
}
} }
return parasitic; return parasitic;
} }
@ -324,4 +327,38 @@ ParasiticAnalysisPt::setCouplingCapFactor(float factor)
coupling_cap_factor_ = 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 } // namespace

View File

@ -1,7 +1,3 @@
%module parasitics
%{
// OpenSTA, Static Timing Analyzer // OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc. // Copyright (c) 2024, Parallax Software, Inc.
// //
@ -18,6 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
%module parasitics
%{
#include "Sta.hh" #include "Sta.hh"
using sta::Sta; using sta::Sta;

View File

@ -37,16 +37,20 @@ proc_redirect read_spef {
set reduce [info exists flags(-reduce)] set reduce [info exists flags(-reduce)]
if { [info exists flags(-quiet)] } { if { [info exists flags(-quiet)] } {
# deprecated 2024-02-08
sta_warn 272 "read_spef -quiet is deprecated." sta_warn 272 "read_spef -quiet is deprecated."
} }
if { [info exists keys(-reduce_to)] } { if { [info exists keys(-reduce_to)] } {
# deprecated 2024-02-08
sta_warn 273 "read_spef -reduce_to is deprecated. Use -reduce instead." sta_warn 273 "read_spef -reduce_to is deprecated. Use -reduce instead."
set reduce 1 set reduce 1
} }
if { [info exists flags(-delete_after_reduce)] } { if { [info exists flags(-delete_after_reduce)] } {
# deprecated 2024-02-08
sta_warn 274 "read_spef -delete_after_reduce is deprecated." sta_warn 274 "read_spef -delete_after_reduce is deprecated."
} }
if { [info exists flags(-save)] } { if { [info exists flags(-save)] } {
# deprecated 2024-02-08
sta_warn 275 "read_spef -save is deprecated." sta_warn 275 "read_spef -save is deprecated."
} }

View File

@ -183,7 +183,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin,
&& resistor != from_res) { && resistor != from_res) {
if (isVisited(onode)) { if (isVisited(onode)) {
// Resistor loop. // 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)); parasitics_->id(resistor));
markLoopResistor(resistor); markLoopResistor(resistor);
} }

View File

@ -80,14 +80,14 @@ ReportParasiticAnnotation::report()
void void
ReportParasiticAnnotation::reportAnnotationCounts() ReportParasiticAnnotation::reportAnnotationCounts()
{ {
report_->reportLine("Found %lu unannotated drivers.", unannotated_.size()); report_->reportLine("Found %zu unannotated drivers.", unannotated_.size());
if (report_unannotated_) { if (report_unannotated_) {
sort(unannotated_, PinPathNameLess(network_)); sort(unannotated_, PinPathNameLess(network_));
for (const Pin *drvr_pin : unannotated_) for (const Pin *drvr_pin : unannotated_)
report_->reportLine(" %s", network_->pathName(drvr_pin)); report_->reportLine(" %s", network_->pathName(drvr_pin));
} }
report_->reportLine("Found %lu partially unannotated drivers.", report_->reportLine("Found %zu partially unannotated drivers.",
partially_annotated_.size()); partially_annotated_.size());
if (report_unannotated_) { if (report_unannotated_) {
sort(partially_annotated_, PinPathNameLess(network_)); sort(partially_annotated_, PinPathNameLess(network_));

View File

@ -55,6 +55,12 @@ readSpefFile(const char *filename,
StaState *sta) StaState *sta)
{ {
bool success = false; 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. // Use zlib to uncompress gzip'd files automagically.
gzFile stream = gzopen(filename, "rb"); gzFile stream = gzopen(filename, "rb");
if (stream) { if (stream) {
@ -120,10 +126,8 @@ SpefReader::~SpefReader()
design_flow_ = nullptr; design_flow_ = nullptr;
} }
for (auto index_name : name_map_) { for (const auto [index, name] : name_map_)
char *name = index_name.second;
stringDelete(name); stringDelete(name);
}
} }
void void
@ -496,7 +500,9 @@ SpefReader::findParasiticNode(char *name,
int id = atoi(id_str); int id = atoi(id_str);
if (local_only if (local_only
&& !network_->isConnected(net, net_)) && !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_); return parasitics_->ensureParasiticNode(parasitic_, net, id, network_);
} }
else else

View File

@ -19,6 +19,7 @@
#include <algorithm> // max #include <algorithm> // max
#include <cmath> // abs #include <cmath> // abs
#include "cudd.h"
#include "Debug.hh" #include "Debug.hh"
#include "EnumNameMap.hh" #include "EnumNameMap.hh"
#include "Hash.hh" #include "Hash.hh"
@ -502,8 +503,6 @@ Power::clockGatePins(const Instance *inst,
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
#if CUDD
PwrActivity PwrActivity
Power::evalActivity(FuncExpr *expr, Power::evalActivity(FuncExpr *expr,
const Instance *inst) const Instance *inst)
@ -586,12 +585,10 @@ Power::evalBddActivity(DdNode *bdd,
const Instance *inst) const Instance *inst)
{ {
float activity = 0.0; float activity = 0.0;
for (auto port_var : bdd_.portVarMap()) { for (const auto [port, var_node] : bdd_.portVarMap()) {
const LibertyPort *port = port_var.first;
const Pin *pin = findLinkPin(inst, port); const Pin *pin = findLinkPin(inst, port);
if (pin) { if (pin) {
PwrActivity var_activity = findActivity(pin); PwrActivity var_activity = findActivity(pin);
DdNode *var_node = port_var.second;
unsigned int var_index = Cudd_NodeReadIndex(var_node); unsigned int var_index = Cudd_NodeReadIndex(var_node);
DdNode *diff = Cudd_bddBooleanDiff(bdd_.cuddMgr(), bdd, var_index); DdNode *diff = Cudd_bddBooleanDiff(bdd_.cuddMgr(), bdd, var_index);
Cudd_Ref(diff); Cudd_Ref(diff);
@ -611,112 +608,6 @@ Power::evalBddActivity(DdNode *bdd,
return activity; 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 void

View File

@ -1,7 +1,3 @@
%module power
%{
// OpenSTA, Static Timing Analyzer // OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc. // Copyright (c) 2024, Parallax Software, Inc.
// //
@ -18,6 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
%module power
%{
#include "Sta.hh" #include "Sta.hh"
#include "power/Power.hh" #include "power/Power.hh"
#include "power/VcdReader.hh" #include "power/VcdReader.hh"
@ -60,7 +59,8 @@ instance_power(Instance *inst,
const Corner *corner) const Corner *corner)
{ {
cmdLinkedNetwork(); cmdLinkedNetwork();
PowerResult power = Sta::sta()->power(inst, corner); Sta *sta = Sta::sta();
PowerResult power = sta->power(inst, corner);
FloatSeq powers; FloatSeq powers;
powers.push_back(power.internal()); powers.push_back(power.internal());
powers.push_back(power.switching()); powers.push_back(power.switching());
@ -73,14 +73,16 @@ void
set_power_global_activity(float activity, set_power_global_activity(float activity,
float duty) float duty)
{ {
Sta::sta()->power()->setGlobalActivity(activity, duty); Power *power = Sta::sta()->power();
power->setGlobalActivity(activity, duty);
} }
void void
set_power_input_activity(float activity, set_power_input_activity(float activity,
float duty) float duty)
{ {
return Sta::sta()->power()->setInputActivity(activity, duty); Power *power = Sta::sta()->power();
return power->setInputActivity(activity, duty);
} }
void void
@ -88,7 +90,8 @@ set_power_input_port_activity(const Port *input_port,
float activity, float activity,
float duty) float duty)
{ {
return Sta::sta()->power()->setInputPortActivity(input_port, activity, duty); Power *power = Sta::sta()->power();
return power->setInputPortActivity(input_port, activity, duty);
} }
void void
@ -96,21 +99,24 @@ set_power_pin_activity(const Pin *pin,
float activity, float activity,
float duty) float duty)
{ {
return Sta::sta()->power()->setUserActivity(pin, activity, duty, Power *power = Sta::sta()->power();
PwrActivityOrigin::user); return power->setUserActivity(pin, activity, duty, PwrActivityOrigin::user);
} }
void void
read_vcd_activities(const char *filename, read_vcd_activities(const char *filename,
const char *scope) const char *scope)
{ {
readVcdActivities(filename, scope, Sta::sta()); Sta *sta = Sta::sta();
cmdLinkedNetwork();
readVcdActivities(filename, scope, sta);
} }
void void
report_vcd_waveforms(const char *filename) report_vcd_waveforms(const char *filename)
{ {
reportVcdWaveforms(filename, Sta::sta()); Sta *sta = Sta::sta();
reportVcdWaveforms(filename, sta);
} }
// debugging // debugging
@ -118,7 +124,8 @@ void
report_vcd_var_values(const char *filename, report_vcd_var_values(const char *filename,
const char *var_name) const char *var_name)
{ {
reportVcdVarValues(filename, var_name, Sta::sta()); Sta *sta = Sta::sta();
reportVcdVarValues(filename, var_name, sta);
} }
%} // inline %} // inline

View File

@ -35,6 +35,10 @@ proc_redirect report_power {
keys {-instances -corner -digits} flags {} keys {-instances -corner -digits} flags {}
check_argc_eq0 "report_power" $args check_argc_eq0 "report_power" $args
if { ![liberty_libraries_exist] } {
sta_error 304 "No liberty libraries have been read."
}
if { [info exists keys(-digits)] } { if { [info exists keys(-digits)] } {
set digits $keys(-digits) set digits $keys(-digits)
check_positive_integer "-digits" $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 } { proc report_power_design { corner digits } {
set power_result [design_power $corner] set power_result [design_power $corner]
set totals [lrange $power_result 0 3] set totals [lrange $power_result 0 3]

View File

@ -105,7 +105,7 @@ ReadVcdActivities::readActivities()
setActivities(); setActivities();
else else
report_->warn(1450, "VCD max time is zero."); 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 void

View File

@ -41,8 +41,8 @@ DeratingFactors::setFactor(PathClkOrData clk_data,
const EarlyLate *early_late, const EarlyLate *early_late,
float factor) float factor)
{ {
for (auto tr1 : rf->range()) for (auto rf1 : rf->range())
factors_[int(clk_data)].setValue(tr1, early_late, factor); factors_[int(clk_data)].setValue(rf1, early_late, factor);
} }
void void

View File

@ -90,7 +90,8 @@ ExceptionPath::ExceptionPath(ExceptionFrom *from,
to_(to), to_(to),
min_max_(min_max), min_max_(min_max),
own_pts_(own_pts), own_pts_(own_pts),
priority_(priority) priority_(priority),
id_(0)
{ {
makeStates(); makeStates();
} }
@ -125,6 +126,12 @@ ExceptionPath::asString(const Network *network) const
return result; return result;
} }
void
ExceptionPath::setId(size_t id)
{
id_ = id;
}
ExceptionPt * ExceptionPt *
ExceptionPath::firstPt() ExceptionPath::firstPt()
{ {
@ -2247,19 +2254,14 @@ ExceptionState::hash() const
return hashSum(exception_->hash(), index_); return hashSum(exception_->hash(), index_);
} }
ExceptionStateLess::ExceptionStateLess(const Network *network) :
network_(network)
{
}
bool bool
ExceptionStateLess::operator()(const ExceptionState *state1, ExceptionStateLess::operator()(const ExceptionState *state1,
const ExceptionState *state2) const const ExceptionState *state2) const
{ {
const ExceptionPath *except1 = state1->exception(); const ExceptionPath *except1 = state1->exception();
const ExceptionPath *except2 = state2->exception(); const ExceptionPath *except2 = state2->exception();
ExceptionPathLess except_less(network_); return except1->id() < except2->id()
return except_less(except1, except2) //return except1 < except2
|| (except1 == except2 || (except1 == except2
&& state1->index() < state2->index()); && state1->index() < state2->index());
} }

View File

@ -20,17 +20,17 @@ namespace sta {
InputDrive::InputDrive() InputDrive::InputDrive()
{ {
for (auto tr_index : RiseFall::rangeIndex()) { for (auto rf_index : RiseFall::rangeIndex()) {
for (auto mm_index : MinMax::rangeIndex()) for (auto mm_index : MinMax::rangeIndex())
drive_cells_[tr_index][mm_index] = nullptr; drive_cells_[rf_index][mm_index] = nullptr;
} }
} }
InputDrive::~InputDrive() InputDrive::~InputDrive()
{ {
for (auto tr_index : RiseFall::rangeIndex()) { for (auto rf_index : RiseFall::rangeIndex()) {
for (auto mm_index : MinMax::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; delete drive_cell;
} }
} }
@ -210,8 +210,8 @@ InputDriveCell::setToPort(const LibertyPort *to_port)
void void
InputDriveCell::setFromSlews(float *from_slews) InputDriveCell::setFromSlews(float *from_slews)
{ {
for (auto tr_index : RiseFall::rangeIndex()) for (auto rf_index : RiseFall::rangeIndex())
from_slews_[tr_index] = from_slews[tr_index]; from_slews_[rf_index] = from_slews[rf_index];
} }
bool bool

View File

@ -110,6 +110,7 @@ Sdc::Sdc(StaState *sta) :
disabled_wire_edges_(network_), disabled_wire_edges_(network_),
disabled_clk_gating_checks_inst_(network_), disabled_clk_gating_checks_inst_(network_),
disabled_clk_gating_checks_pin_(network_), disabled_clk_gating_checks_pin_(network_),
exception_id_(0),
have_thru_hpin_exceptions_(false), have_thru_hpin_exceptions_(false),
first_thru_edge_exceptions_(0, PinPairHash(network_), PinPairEqual()), first_thru_edge_exceptions_(0, PinPairHash(network_), PinPairEqual()),
path_delay_internal_startpoints_(network_), path_delay_internal_startpoints_(network_),
@ -271,15 +272,12 @@ Sdc::deleteConstraints()
inst_min_pulse_width_map_.deleteContentsClear(); inst_min_pulse_width_map_.deleteContentsClear();
clk_min_pulse_width_map_.deleteContentsClear(); clk_min_pulse_width_map_.deleteContentsClear();
for (auto pin_data_check : data_checks_from_map_) { for (auto [pin, checks] : data_checks_from_map_) {
DataCheckSet *checks = pin_data_check.second;
checks->deleteContents(); checks->deleteContents();
delete checks; delete checks;
} }
for (auto pin_data_check : data_checks_to_map_) { for (auto [pin, checks] : data_checks_to_map_)
DataCheckSet *checks = pin_data_check.second;
delete checks; delete checks;
}
input_delays_.deleteContents(); input_delays_.deleteContents();
input_delay_pin_map_.deleteContents(); input_delay_pin_map_.deleteContents();
@ -323,9 +321,7 @@ Sdc::removeNetLoadCaps()
void void
Sdc::removeLibertyAnnotations() Sdc::removeLibertyAnnotations()
{ {
for (auto cell_port : disabled_cell_ports_) { for (auto [cell, disable] : disabled_cell_ports_) {
DisabledCellPorts *disable = cell_port.second;
LibertyCell *cell = disable->cell();
if (disable->all()) if (disable->all())
cell->setIsDisabledConstraint(false); 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 void
Sdc::setAnalysisType(AnalysisType analysis_type) Sdc::setAnalysisType(AnalysisType analysis_type)
{ {
@ -1906,8 +1953,8 @@ void
Sdc::ensureClkGroupExclusions() Sdc::ensureClkGroupExclusions()
{ {
if (clk_group_exclusions_.empty()) { if (clk_group_exclusions_.empty()) {
for (auto name_clk_groups : clk_groups_name_map_) for (const auto [name, clk_groups] : clk_groups_name_map_)
makeClkGroupExclusions(name_clk_groups.second); makeClkGroupExclusions(clk_groups);
} }
} }
@ -2020,8 +2067,7 @@ Sdc::removeClockGroupsLogicallyExclusive(const char *name)
removeClockGroups(groups); removeClockGroups(groups);
} }
else { else {
for (auto name_group : clk_groups_name_map_) { for (const auto [name, groups] : clk_groups_name_map_) {
ClockGroups *groups = name_group.second;
if (groups->logicallyExclusive()) if (groups->logicallyExclusive())
removeClockGroups(groups); removeClockGroups(groups);
} }
@ -2037,8 +2083,7 @@ Sdc::removeClockGroupsPhysicallyExclusive(const char *name)
removeClockGroups(groups); removeClockGroups(groups);
} }
else { else {
for (auto name_group : clk_groups_name_map_) { for (const auto [name, groups] : clk_groups_name_map_) {
ClockGroups *groups = name_group.second;
if (groups->physicallyExclusive()) if (groups->physicallyExclusive())
removeClockGroups(groups); removeClockGroups(groups);
} }
@ -2054,8 +2099,7 @@ Sdc::removeClockGroupsAsynchronous(const char *name)
removeClockGroups(groups); removeClockGroups(groups);
} }
else { else {
for (auto name_group : clk_groups_name_map_) { for (const auto [name, groups] : clk_groups_name_map_) {
ClockGroups *groups = name_group.second;
if (groups->asynchronous()) if (groups->asynchronous())
removeClockGroups(groups); removeClockGroups(groups);
} }
@ -2075,10 +2119,8 @@ Sdc::removeClockGroups(ClockGroups *groups)
void void
Sdc::clockGroupsDeleteClkRefs(Clock *clk) Sdc::clockGroupsDeleteClkRefs(Clock *clk)
{ {
for (auto name_group : clk_groups_name_map_) { for (const auto [name, groups] : clk_groups_name_map_)
ClockGroups *groups = name_group.second;
groups->removeClock(clk); groups->removeClock(clk);
}
clearClkGroupExclusions(); clearClkGroupExclusions();
} }
@ -3983,9 +4025,7 @@ Sdc::clearGroupPathMap()
{ {
// GroupPath exceptions are deleted with other exceptions. // GroupPath exceptions are deleted with other exceptions.
// Delete group_path name strings. // Delete group_path name strings.
for (auto name_groups : group_path_map_) { for (auto [name, groups] : group_path_map_) {
const char *name = name_groups.first;
GroupPathSet *groups = name_groups.second;
stringDelete(name); stringDelete(name);
groups->deleteContents(); groups->deleteContents();
delete groups; delete groups;
@ -4513,6 +4553,7 @@ void
Sdc::recordException(ExceptionPath *exception) Sdc::recordException(ExceptionPath *exception)
{ {
exceptions_.insert(exception); exceptions_.insert(exception);
exception->setId(++exception_id_);
recordMergeHashes(exception); recordMergeHashes(exception);
recordExceptionFirstPts(exception); recordExceptionFirstPts(exception);
checkForThruHpins(exception); checkForThruHpins(exception);
@ -4774,6 +4815,7 @@ void
Sdc::deleteExceptions() Sdc::deleteExceptions()
{ {
exceptions_.deleteContentsClear(); exceptions_.deleteContentsClear();
exception_id_ = 0;
first_from_pin_exceptions_.deleteContentsClear(); first_from_pin_exceptions_.deleteContentsClear();
first_from_clk_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 // but flush all other exception states because they are lower
// priority. // priority.
if (states == nullptr) if (states == nullptr)
states = new ExceptionStateSet(network_); states = new ExceptionStateSet();
states->clear(); states->clear();
states->insert(state); states->insert(state);
// No need to examine other exceptions from this // No need to examine other exceptions from this
@ -5172,7 +5214,7 @@ Sdc::exceptionFromStates(const ExceptionPathSet *exceptions,
return false; return false;
} }
if (states == nullptr) if (states == nullptr)
states = new ExceptionStateSet(network_); states = new ExceptionStateSet();
states->insert(state); states->insert(state);
} }
} }
@ -5219,7 +5261,7 @@ Sdc::filterRegQStates(const Pin *to_pin,
&& exception->matchesFirstPt(to_rf, min_max)) { && exception->matchesFirstPt(to_rf, min_max)) {
ExceptionState *state = exception->firstState(); ExceptionState *state = exception->firstState();
if (states == nullptr) if (states == nullptr)
states = new ExceptionStateSet(network_); states = new ExceptionStateSet();
states->insert(state); states->insert(state);
} }
} }
@ -5263,7 +5305,7 @@ Sdc::exceptionThruStates(const ExceptionPathSet *exceptions,
if (exception->matchesFirstPt(to_rf, min_max)) { if (exception->matchesFirstPt(to_rf, min_max)) {
ExceptionState *state = exception->firstState(); ExceptionState *state = exception->firstState();
if (states == nullptr) if (states == nullptr)
states = new ExceptionStateSet(network_); states = new ExceptionStateSet();
states->insert(state); states->insert(state);
} }
} }

1479
sdc/Sdc.i Normal file

File diff suppressed because it is too large Load Diff

View File

@ -296,50 +296,21 @@ proc all_clocks { } {
################################################################ ################################################################
define_cmd_args "all_inputs" {} define_cmd_args "all_inputs" {[-no_clocks]}
proc all_inputs { } { proc all_inputs { args } {
return [all_ports_for_direction "input"] 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" {} define_cmd_args "all_outputs" {}
proc all_outputs { } { proc all_outputs { args } {
return [all_ports_for_direction "output"] check_argc_eq0 "all_outputs" $args
} return [all_outputs_cmd]
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
}
} }
################################################################ ################################################################
@ -1329,7 +1300,7 @@ proc set_clock_gating_check { args } {
flags {-rise -fall -high -low} flags {-rise -fall -high -low}
check_argc_eq0or1 "set_clock_gating_check" $args 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 "" set active_value ""
if {[info exists flags(-high)] && [info exists flags(-low)]} { 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." sta_error 397 "missing -setup or -hold argument."
} }
if [info exists keys(-hold)] { 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)] { 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] set margin [time_ui_sta $margin]
if { [llength $args] == 0 } { if { [llength $args] == 0 } {
if { $active_value != "" } { if { $active_value != "" } {
sta_error 398 "-high and -low only permitted for pins and instances." 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 } { } elseif { [llength $args] == 1 } {
parse_clk_inst_port_pin_arg [lindex $args 0] clks insts pins 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." sta_error 399 "-high and -low only permitted for pins and instances."
} }
foreach clk $clks { 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 == "" } { if { $active_value == "" } {
set active_value "X" set active_value "X"
} }
foreach pin $pins { 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 $margin $active_value
} }
foreach inst $insts { 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 $margin $active_value
} }
} }
@ -1515,7 +1486,7 @@ proc set_clock_latency { args } {
parse_clk_port_pin_arg $objects clks pins 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 min_max [parse_min_max_all_flags flags]
set pin_clk "NULL" set pin_clk "NULL"
@ -1531,14 +1502,14 @@ proc set_clock_latency { args } {
set early_late [parse_early_late_all_flags flags] set early_late [parse_early_late_all_flags flags]
foreach clk $clks { 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 { foreach pin $pins {
# Source only allowed on clocks and clock pins. # Source only allowed on clocks and clock pins.
if { ![is_clock_src $pin] } { if { ![is_clock_src $pin] } {
sta_error 409 "-source '[get_full_name $pin]' is not a clock pin." sta_error 409 "-source '[get_full_name $pin]' is not a clock pin."
} }
set_clock_insertion_cmd $pin_clk $pin $tr $min_max $early_late $delay set_clock_insertion_cmd $pin_clk $pin $rf $min_max $early_late $delay
} }
} else { } else {
# Latency. # Latency.
@ -1547,10 +1518,10 @@ proc set_clock_latency { args } {
} }
foreach clk $clks { 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 { 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 {} \ parse_key_args "set_clock_transition" args keys {} \
flags {-rise -fall -max -min} 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] set min_max [parse_min_max_all_flags flags]
check_argc_eq2 "set_clock_transition" $args check_argc_eq2 "set_clock_transition" $args
@ -1687,7 +1658,7 @@ proc set_clock_transition { args } {
if { [$clk is_virtual] } { if { [$clk is_virtual] } {
sta_warn 419 "transition time can not be specified for virtual clocks." sta_warn 419 "transition time can not be specified for virtual clocks."
} else { } 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 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 } { proc set_disable_timing_cell { cell from to } {
set from_ports [parse_disable_cell_ports $cell $from] set from_ports [parse_disable_cell_ports $cell $from]
set to_ports [parse_disable_cell_ports $cell $to] set to_ports [parse_disable_cell_ports $cell $to]
@ -2114,7 +2101,7 @@ proc set_disable_timing_cell { cell from to } {
} else { } else {
foreach from_port $from_ports { foreach from_port $from_ports {
foreach to_port $to_ports { foreach to_port $to_ports {
disable_cell $cell $from_port $to_port disable_cell $cell $from_port $to_port
} }
} }
} }
@ -2163,7 +2150,7 @@ proc unset_disable_cmd { cmd cmd_args } {
libcells libports insts ports pins edges timing_arc_sets libcells libports insts ports pins edges timing_arc_sets
if { ([info exists keys(-from)] || [info exists keys(-to)]) \ 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." sta_warn 434 "-from/-to keywords ignored for lib_pin, port and pin arguments."
} }
@ -2206,7 +2193,7 @@ proc unset_disable_timing_cell { cell from to } {
} else { } else {
foreach from_port $from_ports { foreach from_port $from_ports {
foreach to_port $to_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 { } else {
foreach from_port $from_ports { foreach from_port $from_ports {
foreach to_port $to_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." sta_warn 437 "-from, -through or -to required."
} else { } else {
if [info exists flags(-reset_path)] { 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] 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 \ parse_key_args $cmd cmd_args \
keys {-clock -reference_pin} \ keys {-clock -reference_pin} \
flags {-rise -fall -max -min -clock_fall -add_delay \ 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 check_argc_eq2 $cmd $cmd_args
set delay_arg [lindex $cmd_args 0] 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 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 min_max [parse_min_max_all_flags flags]
set add [info exists flags(-add_delay)] set add [info exists flags(-add_delay)]
set source_latency_included [info exists flags(-source_latency_included)] 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 { foreach pin $pins {
if { [$pin is_top_level_port] \ 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]'." sta_warn 440 "$cmd not allowed on [pin_direction $pin] port '[get_full_name $pin]'."
} elseif { $clk != "NULL" && [lsearch [$clk sources] $pin] != -1 } { } elseif { $clk != "NULL" && [lsearch [$clk sources] $pin] != -1 } {
sta_warn 441 "$cmd relative to a clock defined on the same port/pin not allowed." sta_warn 441 "$cmd relative to a clock defined on the same port/pin not allowed."
} else { } else {
$sta_cmd $pin $tr $clk $clk_rf $ref_pin\ $sta_cmd $pin $rf $clk $clk_rf $ref_pin\
$source_latency_included $network_latency_included \ $source_latency_included $network_latency_included \
$min_max $add $delay $min_max $add $delay
} }
} }
} }
@ -2680,11 +2667,11 @@ proc unset_port_delay { cmd swig_cmd cmd_args } {
set clk_rf "rise" 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 min_max [parse_min_max_all_flags flags]
foreach pin $pins { 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 } { proc set_case_analysis { value pins } {
if { !($value == "0" \ if { !($value == "0" \
|| $value == "1" \ || $value == "1" \
|| $value == "zero" \ || $value == "zero" \
|| $value == "one" \ || $value == "one" \
|| $value == "rise" \ || $value == "rise" \
|| $value == "rising" \ || $value == "rising" \
|| $value == "fall" \ || $value == "fall" \
|| $value == "falling") } { || $value == "falling") } {
sta_error 451 "value must be 0, zero, 1, one, rise, rising, fall, or falling." sta_error 451 "value must be 0, zero, 1, one, rise, rising, fall, or falling."
} }
set pins1 [get_port_pins_error "pins" $pins] 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] \ define_cmd_args "set_drive" {[-rise] [-fall] [-min] [-max] \
resistance ports} resistance ports}
proc set_drive { args } { proc set_drive { args } {
parse_key_args "set_drive" args keys {} flags {-rise -fall -min -max} 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] set min_max [parse_min_max_all_check_flags flags]
check_argc_eq2 "set_drive" $args check_argc_eq2 "set_drive" $args
@ -2774,7 +2761,7 @@ proc set_drive { args } {
set res [resistance_ui_sta $res] set res [resistance_ui_sta $res]
set ports [get_ports_error "ports" [lindex $args 1]] set ports [get_ports_error "ports" [lindex $args 1]]
foreach port $ports { 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 } { proc set_driving_cell { args } {
parse_key_args "set_driving_cell" args \ parse_key_args "set_driving_cell" args \
keys {-lib_cell -cell -library -pin -from_pin -multiply_by \ 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} 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] set min_max [parse_min_max_all_flags flags]
# -cell is an undocumented non-sdc alias for -lib_cell. # -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 library [get_liberty_error "library" $keys(-library)]
set cell [$library find_liberty_cell $cell_name] set cell [$library find_liberty_cell $cell_name]
if { $cell == "NULL" } { if { $cell == "NULL" } {
sta_error 452 "cell '$lib_name:$cell_name' not found." sta_error 452 "cell '$lib_name:$cell_name' not found."
} }
} else { } else {
set library "NULL" set library "NULL"
set cell [find_liberty_cell $cell_name] set cell [find_liberty_cell $cell_name]
if { $cell == "NULL" } { if { $cell == "NULL" } {
sta_error 453 "'$cell_name' not found." sta_error 453 "'$cell_name' not found."
} }
} }
} else { } else {
@ -2834,14 +2821,14 @@ proc set_driving_cell { args } {
set port [$port_iter next] set port [$port_iter next]
set dir [liberty_port_direction $port] set dir [liberty_port_direction $port]
if { [port_direction_any_output $dir] } { if { [port_direction_any_output $dir] } {
incr output_count incr output_count
if { $output_count > 1 } { if { $output_count > 1 } {
$port_iter finish $port_iter finish
sta_error 456 "-pin argument required for cells with multiple outputs." sta_error 456 "-pin argument required for cells with multiple outputs."
} }
set to_port $port set to_port $port
# No break. Keep looking for output ports to make sure there # No break. Keep looking for output ports to make sure there
# is only one. # is only one.
} }
} }
$port_iter finish $port_iter finish
@ -2884,14 +2871,14 @@ proc set_driving_cell { args } {
set ports [get_ports_error "ports" [lindex $args 0]] set ports [get_ports_error "ports" [lindex $args 0]]
foreach port $ports { foreach port $ports {
set_drive_cell_cmd $library $cell $port $from_port \ 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 } { proc port_direction_any_output { dir } {
return [expr { $dir == "output" \ return [expr { $dir == "output" \
|| $dir == "bidirect" \ || $dir == "bidirect" \
|| $dir == "tristate" } ] || $dir == "tristate" } ]
} }
################################################################ ################################################################
@ -2911,7 +2898,7 @@ proc set_input_transition { args } {
parse_key_args "set_input_transition" args keys {-clock} \ parse_key_args "set_input_transition" args keys {-clock} \
flags {-rise -fall -max -min -clock_fall} 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] set min_max [parse_min_max_all_flags flags]
@ -2930,7 +2917,7 @@ proc set_input_transition { args } {
} }
foreach port $ports { 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. # -pin_load is the default.
if { $pin_load || (!$pin_load && !$wire_load) } { if { $pin_load || (!$pin_load && !$wire_load) } {
foreach port $ports { 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 } { } elseif { $wire_load } {
foreach port $ports { 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] set objects [lindex $args 1]
parse_clk_cell_port_args $objects clks cells ports 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 {} set path_types {}
if { ![info exists flags(-clock_path)] \ 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. # Derate clk and data if neither -clock_path or -data_path.
set path_types {"clk" "data"} set path_types {"clk" "data"}
} }
@ -3111,17 +3098,17 @@ proc set_max_transition { args } {
} }
if { ($ports != {} || $cells != {}) \ if { ($ports != {} || $cells != {}) \
&& ([info exists flags(-clock_path)] \ && ([info exists flags(-clock_path)] \
|| [info exists flags(-data_path)] || [info exists flags(-data_path)]
|| [info exists flags(-rise)] || [info exists flags(-rise)]
|| [info exists flags(-fall)]) } { || [info exists flags(-fall)]) } {
sta_warn 468 "-data_path, -clock_path, -rise, -fall ignored for ports and designs." 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. # -clock_path/-data_path and transition only apply to clock objects.
foreach path_type $path_types { foreach path_type $path_types {
foreach clk $clks { 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 { foreach cell $cells {
@ -3180,7 +3167,7 @@ define_cmd_args "set_timing_derate" \
proc set_timing_derate { args } { proc set_timing_derate { args } {
parse_key_args "set_timing_derate" args keys {} \ parse_key_args "set_timing_derate" args keys {} \
flags {-rise -fall -early -late -clock -data \ 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 check_argc_eq1or2 "set_timing_derate" $args
set derate [lindex $args 0] set derate [lindex $args 0]
@ -3189,12 +3176,12 @@ proc set_timing_derate { args } {
sta_warn 469 "derating factor greater than 2.0." 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 early_late [parse_early_late_flags flags]
set path_types {} set path_types {}
if { ![info exists flags(-clock)] \ if { ![info exists flags(-clock)] \
&& ![info exists flags(-data)] } { && ![info exists flags(-data)] } {
# Derate clk and data if neither -clock or -data. # Derate clk and data if neither -clock or -data.
lappend path_types "clk" lappend path_types "clk"
lappend path_types "data" lappend path_types "data"
@ -3222,42 +3209,42 @@ proc set_timing_derate { args } {
parse_libcell_inst_net_arg $objects libcells insts nets parse_libcell_inst_net_arg $objects libcells insts nets
if { $nets != {} } { if { $nets != {} } {
if { [info exists flags(-cell_delay)] \ if { [info exists flags(-cell_delay)] \
|| [info exists flags(-cell_check)] } { || [info exists flags(-cell_check)] } {
sta_warn 470 "-cell_delay and -cell_check flags ignored for net objects." sta_warn 470 "-cell_delay and -cell_check flags ignored for net objects."
} }
foreach net $nets { foreach net $nets {
foreach path_type $path_types { foreach path_type $path_types {
set_timing_derate_net_cmd $net $path_type $tr $early_late $derate set_timing_derate_net_cmd $net $path_type $rf $early_late $derate
} }
} }
} }
if { ![info exists flags(-cell_delay)] \ 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. # Cell checks are not derated if no flags are specified.
set derate_types {cell_delay} set derate_types {cell_delay}
} }
foreach derate_type $derate_types { foreach derate_type $derate_types {
foreach path_type $path_types { foreach path_type $path_types {
foreach inst $insts { foreach inst $insts {
set_timing_derate_inst_cmd $inst $derate_type $path_type \ set_timing_derate_inst_cmd $inst $derate_type $path_type \
$tr $early_late $derate $rf $early_late $derate
} }
foreach libcell $libcells { foreach libcell $libcells {
set_timing_derate_cell_cmd $libcell $derate_type $path_type \ set_timing_derate_cell_cmd $libcell $derate_type $path_type \
$tr $early_late $derate $rf $early_late $derate
} }
} }
} }
} else { } else {
if { ![info exists flags(-net_delay)] \ if { ![info exists flags(-net_delay)] \
&& ![info exists flags(-cell_delay)] \ && ![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. # Cell checks are not derated if no flags are specified.
set derate_types {net_delay cell_delay} set derate_types {net_delay cell_delay}
} }
foreach derate_type $derate_types { foreach derate_type $derate_types {
foreach path_type $path_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)] { if [info exists keys(-from)] {
set key "-from" set key "-from"
set tr "rise_fall" set rf "rise_fall"
} elseif [info exists keys(-rise_from)] { } elseif [info exists keys(-rise_from)] {
set key "-rise_from" set key "-rise_from"
set tr "rise" set rf "rise"
} elseif [info exists keys(-fall_from)] { } elseif [info exists keys(-fall_from)] {
set key "-fall_from" set key "-fall_from"
set tr "fall" set rf "fall"
} else { } else {
return "NULL" return "NULL"
} }
@ -3296,7 +3283,7 @@ proc parse_from_arg { keys_var arg_error_var } {
sta_warn 471 "no valid objects specified for $key." sta_warn 471 "no valid objects specified for $key."
return "NULL" 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. # "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 {} set args_rtn {}
while { $args != {} } { while { $args != {} } {
set arg [lindex $args 0] set arg [lindex $args 0]
set tr "" set rf ""
if { $arg == "-through" } { if { $arg == "-through" } {
set tr "rise_fall" set rf "rise_fall"
set key "-through" set key "-through"
} elseif { $arg == "-rise_through" } { } elseif { $arg == "-rise_through" } {
set tr "rise" set rf "rise"
set key "-rise_through" set key "-rise_through"
} elseif { $arg == "-fall_through" } { } elseif { $arg == "-fall_through" } {
set tr "fall" set rf "fall"
set key "-fall_through" set key "-fall_through"
} }
if { $tr != "" } { if { $rf != "" } {
if { [llength $args] > 1 } { if { [llength $args] > 1 } {
set args [lrange $args 1 end] set args [lrange $args 1 end]
set arg [lindex $args 0] set arg [lindex $args 0]
parse_inst_port_pin_net_arg $arg insts pins nets parse_inst_port_pin_net_arg $arg insts pins nets
if {$pins == {} && $insts == {} && $nets == {}} { if {$pins == {} && $insts == {} && $nets == {}} {
upvar 1 $arg_error_var arg_error upvar 1 $arg_error_var arg_error
set arg_error 1 set arg_error 1
sta_warn 472 "no valid objects specified for $key" sta_warn 472 "no valid objects specified for $key"
} else { } else {
lappend thrus [make_exception_thru $pins $nets $insts $tr] lappend thrus [make_exception_thru $pins $nets $insts $rf]
} }
} }
} else { } else {
lappend args_rtn $arg 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 lib [$lib_iter next]
set op_cond [$lib find_operating_conditions $op_cond_name] set op_cond [$lib find_operating_conditions $op_cond_name]
if { $op_cond != "NULL" } { if { $op_cond != "NULL" } {
set_operating_conditions_cmd $op_cond $min_max set_operating_conditions_cmd $op_cond $min_max
set found 1 set found 1
break break
} }
} }
$lib_iter finish $lib_iter finish
@ -3471,8 +3458,8 @@ proc parse_op_cond_analysis_type { key_var } {
if [info exists keys(-analysis_type)] { if [info exists keys(-analysis_type)] {
set analysis_type $keys(-analysis_type) set analysis_type $keys(-analysis_type)
if { $analysis_type == "single" \ if { $analysis_type == "single" \
|| $analysis_type == "bc_wc" \ || $analysis_type == "bc_wc" \
|| $analysis_type == "on_chip_variation" } { || $analysis_type == "on_chip_variation" } {
set_analysis_type_cmd $analysis_type set_analysis_type_cmd $analysis_type
} else { } else {
sta_error 476 "-analysis_type must be single, bc_wc or on_chip_variation." 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 } { proc set_wire_load_mode { mode } {
if { $mode == "top" \ if { $mode == "top" \
|| $mode == "enclosed" \ || $mode == "enclosed" \
|| $mode == "segmented" } { || $mode == "segmented" } {
set_wire_load_mode_cmd $mode set_wire_load_mode_cmd $mode
} else { } else {
sta_error 478 "mode must be top, enclosed or segmented." 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 lib [$lib_iter next]
set wireload [$lib find_wireload $model_name] set wireload [$lib find_wireload $model_name]
if {$wireload != "NULL"} { if {$wireload != "NULL"} {
break; break;
} }
} }
$lib_iter finish $lib_iter finish
@ -3570,7 +3557,7 @@ proc set_wire_load_selection_group { args } {
set lib [$lib_iter next] set lib [$lib_iter next]
set selection [$lib find_wireload_selection $selection_name] set selection [$lib find_wireload_selection $selection_name]
if {$selection != "NULL"} { if {$selection != "NULL"} {
break; break;
} }
} }
$lib_iter finish $lib_iter finish
@ -3686,7 +3673,7 @@ proc set_pvt { args } {
check_argc_eq1 "set_pvt" $args check_argc_eq1 "set_pvt" $args
set insts [get_instances_error "instances" [lindex $args 0]] 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 "min" keys
set_pvt_min_max $insts "max" keys set_pvt_min_max $insts "max" keys
} else { } else {

View File

@ -382,7 +382,7 @@ WriteSdc::writeClocks() const
{ {
// Write clocks in the order they were defined because generated // Write clocks in the order they were defined because generated
// clocks depend on master clocks having been previously defined. // clocks depend on master clocks having been previously defined.
for (auto clk : sdc_->clocks_) { for (const auto clk : sdc_->clocks_) {
if (clk->isGenerated()) if (clk->isGenerated())
writeGeneratedClock(clk); writeGeneratedClock(clk);
else else
@ -852,8 +852,8 @@ void
WriteSdc::writeClockSenses() const WriteSdc::writeClockSenses() const
{ {
Vector<PinClockPair> pin_clks; Vector<PinClockPair> pin_clks;
for (auto iter : sdc_->clk_sense_map_) for (const auto& [pin_clk, sense] : sdc_->clk_sense_map_)
pin_clks.push_back(iter.first); pin_clks.push_back(pin_clk);
// Sort by pin/clk pair so regressions results are stable. // Sort by pin/clk pair so regressions results are stable.
sort(pin_clks, PinClockPairNameLess(sdc_network_)); sort(pin_clks, PinClockPairNameLess(sdc_network_));
@ -937,10 +937,8 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1,
void void
WriteSdc::writeClockGroups() const WriteSdc::writeClockGroups() const
{ {
for (auto &name_groups : sdc_->clk_groups_name_map_) { for (const auto [name, clk_groups] : sdc_->clk_groups_name_map_)
ClockGroups *clk_groups = name_groups.second;
writeClockGroups(clk_groups); writeClockGroups(clk_groups);
}
} }
void void
@ -1408,8 +1406,7 @@ void
WriteSdc::writeDataChecks() const WriteSdc::writeDataChecks() const
{ {
Vector<DataCheck*> checks; Vector<DataCheck*> checks;
for (auto pin_checks : sdc_->data_checks_to_map_) { for (const auto [pin, checks1] : sdc_->data_checks_to_map_) {
DataCheckSet *checks1 = pin_checks.second;
for (DataCheck *check : *checks1) for (DataCheck *check : *checks1)
checks.push_back(check); checks.push_back(check);
} }
@ -1512,9 +1509,7 @@ void
WriteSdc::writeNetLoads() const WriteSdc::writeNetLoads() const
{ {
int corner_index = 0; // missing corner arg int corner_index = 0; // missing corner arg
for (auto net_cap : sdc_->net_wire_cap_maps_[corner_index]) { for (const auto [net, caps] : sdc_->net_wire_cap_maps_[corner_index]) {
const Net *net = net_cap.first;
MinMaxFloatValues &caps = net_cap.second;
float min_cap, max_cap; float min_cap, max_cap;
bool min_exists, max_exists; bool min_exists, max_exists;
caps.value(MinMax::min(), min_cap, min_exists); caps.value(MinMax::min(), min_cap, min_exists);
@ -1725,10 +1720,8 @@ void
WriteSdc::writeNetResistances() const WriteSdc::writeNetResistances() const
{ {
NetSeq nets; NetSeq nets;
for (auto net_res : sdc_->netResistances()) { for (const auto [net, res] : sdc_->netResistances())
const Net *net = net_res.first;
nets.push_back(net); nets.push_back(net);
}
sort(nets, NetPathNameLess(sdc_network_)); sort(nets, NetPathNameLess(sdc_network_));
for (const Net *net : nets) { for (const Net *net : nets) {
float min_res, max_res; float min_res, max_res;
@ -1843,10 +1836,8 @@ void
WriteSdc::sortedLogicValuePins(LogicValueMap &value_map, WriteSdc::sortedLogicValuePins(LogicValueMap &value_map,
PinSeq &pins) const PinSeq &pins) const
{ {
for (auto pin_value : value_map) { for (const auto [pin, value] : value_map)
const Pin *pin = pin_value.first;
pins.push_back(pin); pins.push_back(pin);
}
// Sort pins. // Sort pins.
sort(pins, PinPathNameLess(sdc_network_)); sort(pins, PinPathNameLess(sdc_network_));
} }
@ -1860,9 +1851,7 @@ WriteSdc::writeDeratings() const
if (factors) if (factors)
writeDerating(factors); writeDerating(factors);
for (auto net_derating : sdc_->net_derating_factors_) { for (const auto [net, factors] : sdc_->net_derating_factors_) {
const Net *net = net_derating.first;
DeratingFactorsNet *factors = net_derating.second;
WriteGetNet write_net(net, this); WriteGetNet write_net(net, this);
for (auto early_late : EarlyLate::range()) { for (auto early_late : EarlyLate::range()) {
writeDerating(factors, TimingDerateType::net_delay, early_late, writeDerating(factors, TimingDerateType::net_delay, early_late,
@ -1870,16 +1859,12 @@ WriteSdc::writeDeratings() const
} }
} }
for (auto inst_derating : sdc_->inst_derating_factors_) { for (const auto [inst, factors] : sdc_->inst_derating_factors_) {
const Instance *inst = inst_derating.first;
DeratingFactorsCell *factors = inst_derating.second;
WriteGetInstance write_inst(inst, this); WriteGetInstance write_inst(inst, this);
writeDerating(factors, &write_inst); writeDerating(factors, &write_inst);
} }
for (auto cell_derating : sdc_->cell_derating_factors_) { for (const auto [cell, factors] : sdc_->cell_derating_factors_) {
const LibertyCell *cell = cell_derating.first;
DeratingFactorsCell *factors = cell_derating.second;
WriteGetLibCell write_cell(cell, this); WriteGetLibCell write_cell(cell, this);
writeDerating(factors, &write_cell); writeDerating(factors, &write_cell);
} }
@ -2029,12 +2014,10 @@ WriteSdc::writeVoltages() const
gzprintf(stream_, "set_voltage %.3f\n", voltage_max); gzprintf(stream_, "set_voltage %.3f\n", voltage_max);
} }
for (auto net_volt : sdc_->net_voltage_map_) { for (const auto& [net, volts] : sdc_->net_voltage_map_) {
const Net *net = net_volt.first; volts.value(MinMax::max(), voltage_max, exists_max);
MinMaxFloatValues &values = net_volt.second;
values.value(MinMax::max(), voltage_max, exists_max);
if (exists_max) { if (exists_max) {
values.value(MinMax::min(), voltage_min, exists_min); volts.value(MinMax::min(), voltage_min, exists_min);
if (exists_min) if (exists_min)
gzprintf(stream_, "set_voltage -object_list %s -min %.3f %.3f\n", gzprintf(stream_, "set_voltage -object_list %s -min %.3f %.3f\n",
sdc_network_->pathName(net), sdc_network_->pathName(net),
@ -2065,23 +2048,17 @@ WriteSdc::writeDesignRules() const
void void
WriteSdc::writeMinPulseWidths() const WriteSdc::writeMinPulseWidths() const
{ {
for (auto pin_widths : sdc_->pin_min_pulse_width_map_) { for (const auto [pin, min_widths] : sdc_->pin_min_pulse_width_map_) {
const Pin *pin = pin_widths.first;
RiseFallValues *min_widths = pin_widths.second;
WriteGetPin write_obj(pin, false, this); WriteGetPin write_obj(pin, false, this);
writeMinPulseWidths(min_widths, write_obj); writeMinPulseWidths(min_widths, write_obj);
} }
for (auto inst_widths : sdc_->inst_min_pulse_width_map_) { for (const auto [inst, min_widths] : sdc_->inst_min_pulse_width_map_) {
const Instance *inst = inst_widths.first;
RiseFallValues *min_widths = inst_widths.second;
WriteGetInstance write_obj(inst, this); WriteGetInstance write_obj(inst, this);
writeMinPulseWidths(min_widths, write_obj); writeMinPulseWidths(min_widths, write_obj);
} }
for (auto clk_widths : sdc_->clk_min_pulse_width_map_) { for (const auto [clk, min_widths] : sdc_->clk_min_pulse_width_map_) {
const Clock *clk = clk_widths.first;
RiseFallValues *min_widths = clk_widths.second;
WriteGetClock write_obj(clk, this); WriteGetClock write_obj(clk, this);
writeMinPulseWidths(min_widths, write_obj); writeMinPulseWidths(min_widths, write_obj);
} }
@ -2123,9 +2100,7 @@ WriteSdc::writeMinPulseWidth(const char *hi_low,
void void
WriteSdc::writeLatchBorowLimits() const WriteSdc::writeLatchBorowLimits() const
{ {
for (auto pin_borrow : sdc_->pin_latch_borrow_limit_map_) { for (const auto [pin, limit] : sdc_->pin_latch_borrow_limit_map_) {
const Pin *pin = pin_borrow.first;
float limit = pin_borrow.second;
gzprintf(stream_, "set_max_time_borrow "); gzprintf(stream_, "set_max_time_borrow ");
writeTime(limit); writeTime(limit);
gzprintf(stream_, " "); gzprintf(stream_, " ");
@ -2133,9 +2108,7 @@ WriteSdc::writeLatchBorowLimits() const
gzprintf(stream_, "\n"); gzprintf(stream_, "\n");
} }
for (auto inst_borrow : sdc_->inst_latch_borrow_limit_map_) { for (const auto [inst, limit] : sdc_->inst_latch_borrow_limit_map_) {
const Instance *inst = inst_borrow.first;
float limit = inst_borrow.second;
gzprintf(stream_, "set_max_time_borrow "); gzprintf(stream_, "set_max_time_borrow ");
writeTime(limit); writeTime(limit);
gzprintf(stream_, " "); gzprintf(stream_, " ");
@ -2143,9 +2116,7 @@ WriteSdc::writeLatchBorowLimits() const
gzprintf(stream_, "\n"); gzprintf(stream_, "\n");
} }
for (auto clk_borrow : sdc_->clk_latch_borrow_limit_map_) { for (const auto [clk, limit] : sdc_->clk_latch_borrow_limit_map_) {
const Clock *clk = clk_borrow.first;
float limit = clk_borrow.second;
gzprintf(stream_, "set_max_time_borrow "); gzprintf(stream_, "set_max_time_borrow ");
writeTime(limit); writeTime(limit);
gzprintf(stream_, " "); gzprintf(stream_, " ");
@ -2266,12 +2237,10 @@ WriteSdc::writeCapLimits(const MinMax *min_max,
gzprintf(stream_, " [current_design]\n"); gzprintf(stream_, " [current_design]\n");
} }
for (auto port_limit : sdc_->port_cap_limit_map_) { for (const auto [port, limits] : sdc_->port_cap_limit_map_) {
const Port *port = port_limit.first;
MinMaxFloatValues values = port_limit.second;
float cap; float cap;
bool exists; bool exists;
values.value(min_max, cap, exists); limits.value(min_max, cap, exists);
if (exists) { if (exists) {
gzprintf(stream_, "%s ", cmd); gzprintf(stream_, "%s ", cmd);
writeCapacitance(cap); writeCapacitance(cap);
@ -2281,12 +2250,10 @@ WriteSdc::writeCapLimits(const MinMax *min_max,
} }
} }
for (auto pin_limit : sdc_->pin_cap_limit_map_) { for (const auto [pin, limits] : sdc_->pin_cap_limit_map_) {
const Pin *pin = pin_limit.first;
MinMaxFloatValues values = pin_limit.second;
float cap; float cap;
bool exists; bool exists;
values.value(min_max, cap, exists); limits.value(min_max, cap, exists);
if (exists) { if (exists) {
gzprintf(stream_, "%s ", cmd); gzprintf(stream_, "%s ", cmd);
writeCapacitance(cap); writeCapacitance(cap);

View File

@ -1,7 +1,3 @@
%module sdf
%{
// OpenSTA, Static Timing Analyzer // OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc. // Copyright (c) 2024, Parallax Software, Inc.
// //
@ -18,6 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
%module sdf
%{
#include "sdf/SdfReader.hh" #include "sdf/SdfReader.hh"
#include "sdf/ReportAnnotation.hh" #include "sdf/ReportAnnotation.hh"
#include "sdf/SdfWriter.hh" #include "sdf/SdfWriter.hh"

View File

@ -422,16 +422,20 @@ SdfReader::findPort(const Cell *cell,
} }
void void
SdfReader::timingCheck(TimingRole *role, SdfPortSpec *data_edge, SdfReader::timingCheck(TimingRole *role,
SdfPortSpec *clk_edge, SdfTriple *triple) SdfPortSpec *data_edge,
SdfPortSpec *clk_edge,
SdfTriple *triple)
{ {
const char *data_port_name = data_edge->port(); if (instance_) {
const char *clk_port_name = clk_edge->port(); const char *data_port_name = data_edge->port();
Cell *cell = network_->cell(instance_); const char *clk_port_name = clk_edge->port();
Port *data_port = findPort(cell, data_port_name); Cell *cell = network_->cell(instance_);
Port *clk_port = findPort(cell, clk_port_name); Port *data_port = findPort(cell, data_port_name);
if (data_port && clk_port) Port *clk_port = findPort(cell, clk_port_name);
timingCheck1(role, data_port, data_edge, clk_port, clk_edge, triple); if (data_port && clk_port)
timingCheck1(role, data_port, data_edge, clk_port, clk_edge, triple);
}
deletePortSpec(data_edge); deletePortSpec(data_edge);
deletePortSpec(clk_edge); deletePortSpec(clk_edge);
deleteTriple(triple); deleteTriple(triple);

View File

@ -45,7 +45,7 @@ public:
SdfWriter(StaState *sta); SdfWriter(StaState *sta);
~SdfWriter(); ~SdfWriter();
void write(const char *filename, void write(const char *filename,
Corner *corner, const Corner *corner,
char sdf_divider, char sdf_divider,
bool include_typ, bool include_typ,
int digits, int digits,
@ -122,7 +122,7 @@ private:
void void
writeSdf(const char *filename, writeSdf(const char *filename,
Corner *corner, const Corner *corner,
char sdf_divider, char sdf_divider,
bool include_typ, bool include_typ,
int digits, int digits,
@ -151,7 +151,7 @@ SdfWriter::~SdfWriter()
void void
SdfWriter::write(const char *filename, SdfWriter::write(const char *filename,
Corner *corner, const Corner *corner,
char sdf_divider, char sdf_divider,
bool include_typ, bool include_typ,
int digits, int digits,

View File

@ -23,7 +23,7 @@ class Corner;
void void
writeSdf(const char *filename, writeSdf(const char *filename,
Corner *corner, const Corner *corner,
char divider, char divider,
bool include_typ, bool include_typ,
int digits, int digits,

View File

@ -16,30 +16,11 @@
#include "Bdd.hh" #include "Bdd.hh"
#include "cudd.h"
#include "StaConfig.hh" #include "StaConfig.hh"
#include "Report.hh" #include "Report.hh"
#include "FuncExpr.hh" #include "FuncExpr.hh"
#if CUDD
#include "cudd.h"
#else
#include <cstdint>
#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 { namespace sta {
Bdd::Bdd(const StaState *sta) : Bdd::Bdd(const StaState *sta) :
@ -168,10 +149,8 @@ Bdd::varIndexPort(int var_index)
void void
Bdd::clearVarMap() Bdd::clearVarMap()
{ {
for (auto port_node : bdd_port_var_map_) { for (const auto [port, var_node] : bdd_port_var_map_)
DdNode *var_node = port_node.second;
Cudd_RecursiveDeref(cudd_mgr_, var_node); Cudd_RecursiveDeref(cudd_mgr_, var_node);
}
bdd_port_var_map_.clear(); bdd_port_var_map_.clear();
bdd_var_idx_port_map_.clear(); bdd_var_idx_port_map_.clear();
} }

View File

@ -70,7 +70,7 @@ BfsIterator::clear()
Level level = first_level_; Level level = first_level_;
while (levelLessOrEqual(level, last_level_)) { while (levelLessOrEqual(level, last_level_)) {
VertexSeq &level_vertices = queue_[level]; VertexSeq &level_vertices = queue_[level];
for (auto vertex : level_vertices) { for (Vertex *vertex : level_vertices) {
if (vertex) if (vertex)
vertex->setBfsInQueue(bfs_index_, false); vertex->setBfsInQueue(bfs_index_, false);
} }
@ -88,7 +88,7 @@ BfsIterator::reportEntries(const Network *network)
VertexSeq &level_vertices = queue_[level]; VertexSeq &level_vertices = queue_[level];
if (!level_vertices.empty()) { if (!level_vertices.empty()) {
report_->reportLine("Level %d", level); report_->reportLine("Level %d", level);
for (auto vertex : level_vertices) { for (Vertex *vertex : level_vertices) {
if (vertex) if (vertex)
report_->reportLine(" %s", vertex->name(network)); report_->reportLine(" %s", vertex->name(network));
} }
@ -101,7 +101,7 @@ void
BfsIterator::deleteEntries(Level level) BfsIterator::deleteEntries(Level level)
{ {
VertexSeq &level_vertices = queue_[level]; VertexSeq &level_vertices = queue_[level];
for (auto vertex : level_vertices) { for (Vertex *vertex : level_vertices) {
if (vertex) if (vertex)
vertex->setBfsInQueue(bfs_index_, false); vertex->setBfsInQueue(bfs_index_, false);
} }
@ -281,7 +281,7 @@ BfsIterator::checkInQueue(Vertex *vertex)
{ {
Level level = vertex->level(); Level level = vertex->level();
if (static_cast<Level>(queue_.size()) > level) { if (static_cast<Level>(queue_.size()) > level) {
for (auto v : queue_[level]) { for (Vertex *v : queue_[level]) {
if (v == vertex) { if (v == vertex) {
if (vertex->bfsInQueue(bfs_index_)) if (vertex->bfsInQueue(bfs_index_))
return; return;
@ -308,7 +308,7 @@ BfsIterator::remove(Vertex *vertex)
Level level = vertex->level(); Level level = vertex->level();
if (vertex->bfsInQueue(bfs_index_) if (vertex->bfsInQueue(bfs_index_)
&& static_cast<Level>(queue_.size()) > level) { && static_cast<Level>(queue_.size()) > level) {
for (auto &v : queue_[level]) { for (Vertex *v : queue_[level]) {
if (v == vertex) { if (v == vertex) {
v = nullptr; v = nullptr;
vertex->setBfsInQueue(bfs_index_, false); vertex->setBfsInQueue(bfs_index_, false);

View File

@ -411,7 +411,7 @@ MinPulseWidthCheck::width(const StaState *sta) const
{ {
return closeArrival(sta) + closeOffset(sta) return closeArrival(sta) + closeOffset(sta)
- open_path_.arrival(sta) - open_path_.arrival(sta)
+ commonClkPessimism(sta); + checkCrpr(sta);
} }
float float
@ -458,7 +458,7 @@ minPulseWidth(const Path *path,
} }
Crpr Crpr
MinPulseWidthCheck::commonClkPessimism(const StaState *sta) const MinPulseWidthCheck::checkCrpr(const StaState *sta) const
{ {
CheckCrpr *check_crpr = sta->search()->checkCrpr(); CheckCrpr *check_crpr = sta->search()->checkCrpr();
PathVertex close; PathVertex close;

View File

@ -80,7 +80,7 @@ public:
float closeOffset(const StaState *sta) const; float closeOffset(const StaState *sta) const;
const ClockEdge *openClkEdge(const StaState *sta) const; const ClockEdge *openClkEdge(const StaState *sta) const;
const ClockEdge *closeClkEdge(const StaState *sta) const; const ClockEdge *closeClkEdge(const StaState *sta) const;
Crpr commonClkPessimism(const StaState *sta) const; Crpr checkCrpr(const StaState *sta) const;
protected: protected:
// Open path of the pulse. // Open path of the pulse.

View File

@ -281,8 +281,7 @@ ClkSkews::findWorstClkSkew(const Corner *corner,
clks.push_back(clk); clks.push_back(clk);
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold, include_internal_latency); ClkSkewMap skews = findClkSkew(clks, corner, setup_hold, include_internal_latency);
float worst_skew = 0.0; float worst_skew = 0.0;
for (auto clk_skew_itr : skews) { for (const auto& [clk, clk_skew] : skews) {
ClkSkew &clk_skew = clk_skew_itr.second;
float skew = clk_skew.skew(); float skew = clk_skew.skew();
if (abs(skew) > abs(worst_skew)) if (abs(skew) > abs(worst_skew))
worst_skew = skew; worst_skew = skew;
@ -318,9 +317,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks,
// Reduce skews from each register source. // Reduce skews from each register source.
for (size_t i = 0; i < partial_skews.size(); i++) { for (size_t i = 0; i < partial_skews.size(); i++) {
for (auto clk_skew_itr : partial_skews[i]) { for (auto& [clk, partial_skew] : partial_skews[i]) {
const Clock *clk = clk_skew_itr.first;
auto partial_skew = clk_skew_itr.second;
auto ins = skews.insert(std::make_pair(clk, partial_skew)); auto ins = skews.insert(std::make_pair(clk, partial_skew));
if (!ins.second) { if (!ins.second) {
ClkSkew &final_skew = ins.first->second; ClkSkew &final_skew = ins.first->second;

Some files were not shown because too many files have changed in this diff Show More