Merge remote-tracking branch 'parallax/master'
Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
This commit is contained in:
commit
9a490b1b13
|
|
@ -24,13 +24,12 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14)
|
|||
cmake_policy(SET CMP0086 NEW)
|
||||
endif()
|
||||
|
||||
project(STA VERSION 2.5.0
|
||||
project(STA VERSION 2.6.0
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
option(USE_CUDD "Use CUDD BDD package")
|
||||
option(CUDD_DIR "CUDD BDD package directory")
|
||||
option(USE_TCL_READLINE "Use TCL readliine package")
|
||||
option(USE_TCL_READLINE "Use TCL readliine package" ON)
|
||||
option(USE_SANITIZE "Compile with santize address enabled")
|
||||
|
||||
# Turn on to debug compiler args.
|
||||
|
|
@ -69,7 +68,6 @@ set(STA_SOURCE
|
|||
dcalc/ArnoldiDelayCalc.cc
|
||||
dcalc/ArnoldiReduce.cc
|
||||
dcalc/CcsCeffDelayCalc.cc
|
||||
dcalc/CcsSimDelayCalc.cc
|
||||
dcalc/DcalcAnalysisPt.cc
|
||||
dcalc/DelayCalc.cc
|
||||
dcalc/DelayCalcBase.cc
|
||||
|
|
@ -195,8 +193,10 @@ set(STA_SOURCE
|
|||
search/VisitPathEnds.cc
|
||||
search/VisitPathGroupVertices.cc
|
||||
search/WorstSlack.cc
|
||||
search/WritePathSpice.cc
|
||||
search/WriteSpice.cc
|
||||
|
||||
spice/WritePathSpice.cc
|
||||
spice/WriteSpice.cc
|
||||
spice/Xyce.cc
|
||||
|
||||
power/Power.cc
|
||||
power/ReadVcdActivities.cc
|
||||
|
|
@ -233,22 +233,22 @@ set(STA_TCL_FILES
|
|||
tcl/Util.tcl
|
||||
tcl/CmdArgs.tcl
|
||||
tcl/CmdUtil.tcl
|
||||
tcl/Graph.tcl
|
||||
tcl/Liberty.tcl
|
||||
tcl/Link.tcl
|
||||
tcl/Network.tcl
|
||||
tcl/NetworkEdit.tcl
|
||||
tcl/Property.tcl
|
||||
tcl/Sdc.tcl
|
||||
tcl/Search.tcl
|
||||
tcl/Sta.tcl
|
||||
tcl/Splash.tcl
|
||||
tcl/Variables.tcl
|
||||
tcl/WritePathSpice.tcl
|
||||
dcalc/DelayCalc.tcl
|
||||
graph/Graph.tcl
|
||||
liberty/Liberty.tcl
|
||||
network/Link.tcl
|
||||
network/Network.tcl
|
||||
network/NetworkEdit.tcl
|
||||
parasitics/Parasitics.tcl
|
||||
power/Power.tcl
|
||||
sdc/Sdc.tcl
|
||||
sdf/Sdf.tcl
|
||||
search/Search.tcl
|
||||
spice/WriteSpice.tcl
|
||||
verilog/Verilog.tcl
|
||||
)
|
||||
|
||||
|
|
@ -258,8 +258,7 @@ set(STA_TCL_FILES
|
|||
#
|
||||
################################################################
|
||||
|
||||
# Earlier versions of flex use 'register' declarations that are deprecated
|
||||
# in c++11 and illegal in c++17.
|
||||
# Earlier versions of flex use 'register' declarations that are illegal in c++17.
|
||||
#find_package(FLEX 2.6.4)
|
||||
find_package(FLEX)
|
||||
find_package(BISON)
|
||||
|
|
@ -371,30 +370,25 @@ find_package(Eigen3 REQUIRED)
|
|||
#
|
||||
# Locate CUDD bdd package.
|
||||
#
|
||||
set(CUDD 0)
|
||||
if (USE_CUDD)
|
||||
find_library(CUDD_LIB
|
||||
NAME cudd
|
||||
PATHS ${CUDD_DIR}
|
||||
PATH_SUFFIXES lib lib/cudd cudd/.libs
|
||||
)
|
||||
if (CUDD_LIB)
|
||||
message(STATUS "CUDD library: ${CUDD_LIB}")
|
||||
get_filename_component(CUDD_LIB_DIR "${CUDD_LIB}" PATH)
|
||||
get_filename_component(CUDD_LIB_PARENT1 "${CUDD_LIB_DIR}" PATH)
|
||||
find_file(CUDD_HEADER cudd.h
|
||||
PATHS ${CUDD_LIB_PARENT1} ${CUDD_LIB_PARENT1}/include ${CUDD_LIB_PARENT1}/include/cudd)
|
||||
if (CUDD_HEADER)
|
||||
get_filename_component(CUDD_INCLUDE "${CUDD_HEADER}" PATH)
|
||||
message(STATUS "CUDD header: ${CUDD_HEADER}")
|
||||
# Referenced by StaConfig.hh.cmake
|
||||
set(CUDD 1)
|
||||
else()
|
||||
message(STATUS "CUDD header: not found")
|
||||
endif()
|
||||
find_library(CUDD_LIB
|
||||
NAME cudd
|
||||
PATHS ${CUDD_DIR}
|
||||
PATH_SUFFIXES lib lib/cudd cudd/.libs
|
||||
)
|
||||
if (CUDD_LIB)
|
||||
message(STATUS "CUDD library: ${CUDD_LIB}")
|
||||
get_filename_component(CUDD_LIB_DIR "${CUDD_LIB}" PATH)
|
||||
get_filename_component(CUDD_LIB_PARENT1 "${CUDD_LIB_DIR}" PATH)
|
||||
find_file(CUDD_HEADER cudd.h
|
||||
PATHS ${CUDD_LIB_PARENT1} ${CUDD_LIB_PARENT1}/include ${CUDD_LIB_PARENT1}/include/cudd)
|
||||
if (CUDD_HEADER)
|
||||
get_filename_component(CUDD_INCLUDE "${CUDD_HEADER}" PATH)
|
||||
message(STATUS "CUDD header: ${CUDD_HEADER}")
|
||||
else()
|
||||
message(STATUS "CUDD library: not found")
|
||||
message(STATUS "CUDD header: not found")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "CUDD library: not found")
|
||||
endif()
|
||||
|
||||
if("${SSTA}" STREQUAL "")
|
||||
|
|
@ -426,23 +420,24 @@ set_property(SOURCE ${STA_SWIG_FILE}
|
|||
PROPERTY SWIG_FLAGS
|
||||
-module sta
|
||||
-namespace -prefix sta
|
||||
-I${STA_HOME}/tcl
|
||||
-I${STA_HOME}/sdf
|
||||
-I${STA_HOME}/dcalc
|
||||
-I${STA_HOME}/parasitics
|
||||
-I${STA_HOME}/power
|
||||
-I${STA_HOME}/verilog
|
||||
-I${STA_HOME}
|
||||
)
|
||||
|
||||
set(SWIG_FILES
|
||||
${STA_HOME}/dcalc/DelayCalc.i
|
||||
${STA_HOME}/graph/Graph.i
|
||||
${STA_HOME}/liberty/Liberty.i
|
||||
${STA_HOME}/network/Network.i
|
||||
${STA_HOME}/network/NetworkEdit.i
|
||||
${STA_HOME}/parasitics/Parasitics.i
|
||||
${STA_HOME}/power/Power.i
|
||||
${STA_HOME}/sdc/Sdc.i
|
||||
${STA_HOME}/sdf/Sdf.i
|
||||
${STA_HOME}/search/Search.i
|
||||
${STA_HOME}/spice/WriteSpice.i
|
||||
${STA_HOME}/tcl/Exception.i
|
||||
${STA_HOME}/tcl/StaTcl.i
|
||||
${STA_HOME}/tcl/StaTclTypes.i
|
||||
${STA_HOME}/tcl/NetworkEdit.i
|
||||
${STA_HOME}/util/Util.i
|
||||
${STA_HOME}/verilog/Verilog.i
|
||||
)
|
||||
|
||||
|
|
@ -463,7 +458,6 @@ set_source_files_properties(${STA_SWIG_CXX_FILE}
|
|||
# No simple way to modify the swig template that emits code full of warnings
|
||||
# so suppress them.
|
||||
COMPILE_OPTIONS "-Wno-cast-qual;-Wno-missing-braces;-Wno-deprecated-declarations"
|
||||
INCLUDE_DIRECTORIES "${CUDD_INCLUDE}"
|
||||
)
|
||||
|
||||
target_link_libraries(sta_swig
|
||||
|
|
@ -515,6 +509,7 @@ target_link_libraries(OpenSTA
|
|||
Eigen3::Eigen
|
||||
${TCL_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${CUDD_LIB}
|
||||
)
|
||||
|
||||
if (TCL_READLINE_LIBRARY)
|
||||
|
|
@ -525,9 +520,7 @@ if (ZLIB_LIBRARIES)
|
|||
target_link_libraries(OpenSTA ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if (CUDD_LIB)
|
||||
target_link_libraries(OpenSTA ${CUDD_LIB})
|
||||
endif()
|
||||
target_link_libraries(OpenSTA )
|
||||
|
||||
target_include_directories(OpenSTA
|
||||
PUBLIC
|
||||
|
|
@ -559,7 +552,7 @@ target_compile_options(OpenSTA
|
|||
|
||||
# Disable compiler specific extensions like gnu++11.
|
||||
set_target_properties(OpenSTA PROPERTIES CXX_EXTENSIONS OFF)
|
||||
target_compile_features(OpenSTA PUBLIC cxx_std_11)
|
||||
target_compile_features(OpenSTA PUBLIC cxx_std_17)
|
||||
|
||||
###########################################################
|
||||
# Executable
|
||||
|
|
|
|||
55
README.md
55
README.md
|
|
@ -85,15 +85,15 @@ The build dependency versions are show below. Other versions may
|
|||
work, but these are the versions used for development.
|
||||
|
||||
```
|
||||
from Ubuntu Macos
|
||||
22.04.2 14.4.1
|
||||
cmake 3.10.2 3.24.2 3.29.2
|
||||
clang 9.1.0 15.0.0
|
||||
gcc 3.3.2 11.4.0
|
||||
tcl 8.4 8.6 8.6.6
|
||||
swig 1.3.28 4.1.0 4.2.1
|
||||
bison 1.35 3.8.2 3.8.2
|
||||
flex 2.5.4 2.6.4 2.6.4
|
||||
Ubuntu Macos
|
||||
22.04.2 14.5
|
||||
cmake 3.24.2 3.29.2
|
||||
clang 15.0.0
|
||||
gcc 11.4.0
|
||||
tcl 8.6 8.6.6
|
||||
swig 4.1.0 4.1.1
|
||||
bison 3.8.2 3.8.2
|
||||
flex 2.6.4 2.6.4
|
||||
```
|
||||
|
||||
Note that flex versions before 2.6.4 contain 'register' declarations that
|
||||
|
|
@ -101,38 +101,47 @@ are illegal in c++17.
|
|||
|
||||
External library dependencies:
|
||||
```
|
||||
from Ubuntu Macos
|
||||
eigen 3.4 .0 3.4.0 required
|
||||
tclreadline 2.3.8 optional
|
||||
libz 1.1.4 1.2.5 1.2.8 optional
|
||||
cudd 2.4.1 3.0.0 optional
|
||||
Ubuntu Macos license
|
||||
eigen 3.4.0 3.4.0 MPL2 required
|
||||
cudd 3.0.0 3.0.0 BSD required
|
||||
tclreadline 2.3.8 2.3.8 BSD optional
|
||||
zLib 1.2.5 1.2.8 zlib optional
|
||||
```
|
||||
|
||||
The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html)
|
||||
links the GNU readline library to the TCL interpreter for command line editing
|
||||
On OSX, Homebrew does not support tclreadline, but the macports system does
|
||||
(see https://www.macports.org). To enable TCL readline support use the following
|
||||
Cmake option:
|
||||
Cmake option: See (https://tclreadline.sourceforge.net/) for TCL readline
|
||||
documentation. To change the overly verbose default prompt, add something this
|
||||
to your ~/.sta init file:
|
||||
|
||||
```
|
||||
cmake .. -DUSE_TCL_READLINE=ON
|
||||
if { ![catch {package require tclreadline}] } {
|
||||
proc tclreadline::prompt1 {} {
|
||||
return "> "
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Zlib library is an optional. If CMake finds libz, OpenSTA can
|
||||
read Verilog, SDF, SPF, and SPEF files compressed with gzip.
|
||||
read Liberty, Verilog, SDF, SPF, and SPEF files compressed with gzip.
|
||||
|
||||
CUDD is a binary decision diageram (BDD) package that is used to
|
||||
improve conditional timing arc handling. OpenSTA does not require it
|
||||
to be installed. It is available
|
||||
to be installed, but it improves constant propagation, power activity propagation
|
||||
and spice netlist generation if it is installed.
|
||||
|
||||
CUDD is available
|
||||
[here](https://www.davidkebo.com/source/cudd_versions/cudd-3.0.0.tar.gz)
|
||||
or [here](https://sourceforge.net/projects/cudd-mirror/).
|
||||
|
||||
Note that the file hierarchy of the CUDD installation changed with version 3.0.
|
||||
Some changes to CMakeLists.txt are required to support older versions.
|
||||
Use the CUDD_DIR option to set the install directory of the CUDD
|
||||
library if it is not in one of the normal system install directories.
|
||||
|
||||
Use the USE_CUDD option to look for the cudd library.
|
||||
Use the CUDD_DIR option to set the install directory if it is not in
|
||||
one of the normal install directories.
|
||||
```
|
||||
cmake -DCUDD_DIR=$HOME/stax/cudd-3.0.0 .."
|
||||
```
|
||||
|
||||
When building CUDD you may use the `--prefix ` option to `configure` to
|
||||
install in a location other than the default (`/usr/local/lib`).
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <cstdlib> // exit
|
||||
#include <filesystem>
|
||||
#include <tcl.h>
|
||||
#if TCL_READLINE
|
||||
#include <tclreadline.h>
|
||||
|
|
@ -39,7 +40,6 @@ using sta::evalTclInit;
|
|||
using sta::sourceTclFile;
|
||||
using sta::parseThreadsArg;
|
||||
using sta::tcl_inits;
|
||||
using sta::is_regular_file;
|
||||
|
||||
// Swig uses C linkage for init functions.
|
||||
extern "C" {
|
||||
|
|
@ -133,15 +133,15 @@ staTclAppInit(int argc,
|
|||
string init_path = home;
|
||||
init_path += "/";
|
||||
init_path += init_filename;
|
||||
if (is_regular_file(init_path.c_str()))
|
||||
if (std::filesystem::is_regular_file(init_path.c_str()))
|
||||
sourceTclFile(init_path.c_str(), true, true, interp);
|
||||
}
|
||||
}
|
||||
|
||||
bool exit_after_cmd_file = findCmdLineFlag(argc, argv, "-exit");
|
||||
|
||||
if (argc > 2 ||
|
||||
(argc > 1 && argv[1][0] == '-')) {
|
||||
if (argc > 2
|
||||
|| (argc > 1 && argv[1][0] == '-')) {
|
||||
showUsage(argv[0], init_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
|
|
|||
24
app/StaApp.i
24
app/StaApp.i
|
|
@ -16,12 +16,18 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
%include "Exception.i"
|
||||
%include "StaTclTypes.i"
|
||||
%include "StaTcl.i"
|
||||
%include "Verilog.i"
|
||||
%include "NetworkEdit.i"
|
||||
%include "Sdf.i"
|
||||
%include "DelayCalc.i"
|
||||
%include "Parasitics.i"
|
||||
%include "Power.i"
|
||||
%include "tcl/Exception.i"
|
||||
%include "tcl/StaTclTypes.i"
|
||||
%include "dcalc/DelayCalc.i"
|
||||
%include "graph/Graph.i"
|
||||
%include "liberty/Liberty.i"
|
||||
%include "network/Network.i"
|
||||
%include "network/NetworkEdit.i"
|
||||
%include "parasitics/Parasitics.i"
|
||||
%include "power/Power.i"
|
||||
%include "sdc/Sdc.i"
|
||||
%include "sdf/Sdf.i"
|
||||
%include "search/Search.i"
|
||||
%include "util/Util.i"
|
||||
%include "spice/WriteSpice.i"
|
||||
%include "verilog/Verilog.i"
|
||||
|
|
|
|||
|
|
@ -51,10 +51,11 @@ findCmdLineFlag(int &argc,
|
|||
for (int i = 1; i < argc; i++) {
|
||||
char *arg = argv[i];
|
||||
if (stringEq(arg, flag)) {
|
||||
// remove flag from argv.
|
||||
// Remove flag from argv.
|
||||
for (int j = i + 1; j < argc; j++, i++)
|
||||
argv[i] = argv[j];
|
||||
argc--;
|
||||
argv[argc] = nullptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -70,10 +71,11 @@ findCmdLineKey(int &argc,
|
|||
char *arg = argv[i];
|
||||
if (stringEq(arg, key) && i + 1 < argc) {
|
||||
char *value = argv[i + 1];
|
||||
// remove key and value from argv.
|
||||
// Remove key and value from argv.
|
||||
for (int j = i + 2; j < argc; j++, i++)
|
||||
argv[i] = argv[j];
|
||||
argc -= 2;
|
||||
argv[argc] = nullptr;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,30 +14,64 @@
|
|||
// 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 <memory>
|
||||
|
||||
#include "ArcDcalcWaveforms.hh"
|
||||
|
||||
#include "Report.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "Network.hh"
|
||||
#include "Graph.hh"
|
||||
#include "ArcDelayCalc.hh"
|
||||
#include "DcalcAnalysisPt.hh"
|
||||
#include "GraphDelayCalc.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
Table1
|
||||
ArcDcalcWaveforms::inputWaveform(const Pin *,
|
||||
const RiseFall *,
|
||||
const Corner *,
|
||||
const MinMax *)
|
||||
{
|
||||
return Table1();
|
||||
}
|
||||
using std::make_shared;
|
||||
|
||||
Table1
|
||||
ArcDcalcWaveforms::drvrRampWaveform(const Pin *,
|
||||
const RiseFall *,
|
||||
const Pin *,
|
||||
const RiseFall *,
|
||||
const Pin *,
|
||||
const Corner *,
|
||||
const MinMax *)
|
||||
Waveform
|
||||
ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg,
|
||||
const DcalcAnalysisPt *dcalc_ap,
|
||||
const StaState *sta)
|
||||
{
|
||||
return Table1();
|
||||
const Network *network = sta->network();
|
||||
Graph *graph = sta->graph();
|
||||
Report *report = sta->report();
|
||||
const Pin *in_pin = dcalc_arg.inPin();
|
||||
LibertyPort *port = network->libertyPort(in_pin);
|
||||
if (port) {
|
||||
const RiseFall *in_rf = dcalc_arg.inEdge();
|
||||
DriverWaveform *driver_waveform = port->driverWaveform(in_rf);
|
||||
if (driver_waveform) {
|
||||
const Vertex *in_vertex = graph->pinLoadVertex(in_pin);
|
||||
GraphDelayCalc *graph_dcalc = sta->graphDelayCalc();
|
||||
Slew in_slew = graph_dcalc->edgeFromSlew(in_vertex, in_rf,
|
||||
dcalc_arg.arc()->role(), dcalc_ap);
|
||||
LibertyLibrary *library = port->libertyLibrary();
|
||||
float vdd;
|
||||
bool vdd_exists;
|
||||
library->supplyVoltage("VDD", vdd, vdd_exists);
|
||||
if (!vdd_exists)
|
||||
report->error(1751, "VDD not defined in library %s", library->name());
|
||||
Waveform in_waveform = driver_waveform->waveform(in_slew);
|
||||
// Delay time axis.
|
||||
FloatSeq *time_values = new FloatSeq;
|
||||
for (float time : *in_waveform.axis1()->values())
|
||||
time_values->push_back(time + dcalc_arg.inputDelay());
|
||||
TableAxisPtr time_axis = make_shared<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
|
||||
|
|
|
|||
|
|
@ -22,37 +22,25 @@
|
|||
|
||||
namespace sta {
|
||||
|
||||
class StaState;
|
||||
class Corner;
|
||||
class DcalcAnalysisPt;
|
||||
class ArcDcalcArg;
|
||||
|
||||
// Abstract class for the graph delay calculator traversal to interface
|
||||
// Abstract class for delay calculation waveforms for ploting.
|
||||
class ArcDcalcWaveforms
|
||||
{
|
||||
public:
|
||||
virtual Table1 inputWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
virtual Table1 drvrWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) = 0;
|
||||
virtual Table1 loadWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) = 0;
|
||||
virtual Table1 drvrRampWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
// Record waveform for drvr/load pin.
|
||||
virtual void watchPin(const Pin *pin) = 0;
|
||||
virtual void clearWatchPins() = 0;
|
||||
virtual PinSeq watchPins() const = 0;
|
||||
virtual Waveform watchWaveform(const Pin *pin) = 0;
|
||||
|
||||
protected:
|
||||
Waveform inputWaveform(ArcDcalcArg &dcalc_arg,
|
||||
const DcalcAnalysisPt *dcalc_ap,
|
||||
const StaState *sta);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@
|
|||
|
||||
#include "ArcDelayCalc.hh"
|
||||
|
||||
#include "Units.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "Network.hh"
|
||||
#include "Graph.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -48,12 +50,67 @@ ArcDelayCalc::gateDelay(const TimingArc *arc,
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
ArcDcalcArg
|
||||
makeArcDcalcArg(const char *inst_name,
|
||||
const char *in_port_name,
|
||||
const char *in_rf_name,
|
||||
const char *drvr_port_name,
|
||||
const char *drvr_rf_name,
|
||||
const char *input_delay_str,
|
||||
const StaState *sta)
|
||||
{
|
||||
Report *report = sta->report();
|
||||
const Network *network = sta->sdcNetwork();
|
||||
const Instance *inst = network->findInstance(inst_name);
|
||||
if (inst) {
|
||||
const Pin *in_pin = network->findPin(inst, in_port_name);
|
||||
if (in_pin) {
|
||||
const RiseFall *in_rf = RiseFall::find(in_rf_name);
|
||||
if (in_rf) {
|
||||
const Pin *drvr_pin = network->findPin(inst, drvr_port_name);
|
||||
if (drvr_pin) {
|
||||
const RiseFall *drvr_rf = RiseFall::find(drvr_rf_name);
|
||||
if (drvr_rf) {
|
||||
float input_delay = strtof(input_delay_str, nullptr);
|
||||
input_delay = sta->units()->timeUnit()->userToSta(input_delay);
|
||||
|
||||
const Graph *graph = sta->graph();
|
||||
Edge *edge;
|
||||
const TimingArc *arc;
|
||||
graph->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc);
|
||||
if (edge)
|
||||
return ArcDcalcArg(in_pin, drvr_pin, edge, arc, input_delay);
|
||||
else {
|
||||
const Network *network = sta->network();
|
||||
const Instance *inst = network->instance(in_pin);
|
||||
report->warn(2100, "no timing arc for %s input/driver pins.",
|
||||
network->pathName(inst));
|
||||
}
|
||||
}
|
||||
else
|
||||
report->warn(2101, "%s not a valid rise/fall.", drvr_rf_name);
|
||||
}
|
||||
else
|
||||
report->warn(2102, "Pin %s/%s not found.", inst_name, drvr_port_name);
|
||||
}
|
||||
else
|
||||
report->warn(2103, "%s not a valid rise/fall.", in_rf_name);
|
||||
}
|
||||
else
|
||||
report->warn(2104, "Pin %s/%s not found.", inst_name, in_port_name);
|
||||
}
|
||||
else
|
||||
report->warn(2105, "Instance %s not found.", inst_name);
|
||||
return ArcDcalcArg();
|
||||
}
|
||||
|
||||
ArcDcalcArg::ArcDcalcArg() :
|
||||
in_pin_(nullptr),
|
||||
drvr_pin_(nullptr),
|
||||
edge_(nullptr),
|
||||
arc_(nullptr),
|
||||
in_slew_(0.0),
|
||||
load_cap_(0.0),
|
||||
parasitic_(nullptr),
|
||||
input_delay_(0.0)
|
||||
{
|
||||
|
|
@ -64,12 +121,14 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
|
|||
Edge *edge,
|
||||
const TimingArc *arc,
|
||||
const Slew in_slew,
|
||||
float load_cap,
|
||||
const Parasitic *parasitic) :
|
||||
in_pin_(in_pin),
|
||||
drvr_pin_(drvr_pin),
|
||||
edge_(edge),
|
||||
arc_(arc),
|
||||
in_slew_(in_slew),
|
||||
load_cap_(load_cap),
|
||||
parasitic_(parasitic),
|
||||
input_delay_(0.0)
|
||||
{
|
||||
|
|
@ -85,6 +144,7 @@ ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
|
|||
edge_(edge),
|
||||
arc_(arc),
|
||||
in_slew_(0.0),
|
||||
load_cap_(0.0),
|
||||
parasitic_(nullptr),
|
||||
input_delay_(input_delay)
|
||||
{
|
||||
|
|
@ -96,6 +156,7 @@ ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) :
|
|||
edge_(arg.edge_),
|
||||
arc_(arg.arc_),
|
||||
in_slew_(arg.in_slew_),
|
||||
load_cap_(arg.load_cap_),
|
||||
parasitic_(arg.parasitic_),
|
||||
input_delay_(arg.input_delay_)
|
||||
{
|
||||
|
|
@ -107,6 +168,12 @@ ArcDcalcArg::inEdge() const
|
|||
return arc_->fromEdge()->asRiseFall();
|
||||
}
|
||||
|
||||
Vertex *
|
||||
ArcDcalcArg::drvrVertex(const Graph *graph) const
|
||||
{
|
||||
return edge_->to(graph);
|
||||
}
|
||||
|
||||
LibertyCell *
|
||||
ArcDcalcArg::drvrCell() const
|
||||
{
|
||||
|
|
@ -150,6 +217,18 @@ ArcDcalcArg::setParasitic(const Parasitic *parasitic)
|
|||
parasitic_ = parasitic;
|
||||
}
|
||||
|
||||
void
|
||||
ArcDcalcArg::setLoadCap(float load_cap)
|
||||
{
|
||||
load_cap_ = load_cap;
|
||||
}
|
||||
|
||||
void
|
||||
ArcDcalcArg::setInputDelay(float input_delay)
|
||||
{
|
||||
input_delay_ = input_delay;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
ArcDcalcResult::ArcDcalcResult() :
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ public:
|
|||
ArnoldiDelayCalc(StaState *sta);
|
||||
virtual ~ArnoldiDelayCalc();
|
||||
ArcDelayCalc *copy() override;
|
||||
const char *name() const override { return "arnoldi"; }
|
||||
Parasitic *findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
|
|
@ -382,7 +383,7 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
ConcreteParasitic *cparasitic =
|
||||
reinterpret_cast<ConcreteParasitic*>(const_cast<Parasitic*>(parasitic));
|
||||
rcmodel_ = dynamic_cast<rcmodel*>(cparasitic);
|
||||
GateTableModel *table_model = gateTableModel(arc, dcalc_ap);
|
||||
GateTableModel *table_model = arc->gateTableModel(dcalc_ap);
|
||||
if (table_model && rcmodel_) {
|
||||
const Pvt *pvt = pinPvt(drvr_pin, dcalc_ap);
|
||||
return gateDelaySlew(drvr_cell, arc, table_model, in_slew, load_pin_index_map, pvt);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
|
|||
// Includes the Vh:Vdd region.
|
||||
region_count_(0),
|
||||
vl_fail_(false),
|
||||
watch_pin_values_(network_),
|
||||
capacitance_unit_(units_->capacitanceUnit()),
|
||||
table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
|
||||
{
|
||||
|
|
@ -82,7 +83,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
parasitic_ = parasitic;
|
||||
output_waveforms_ = nullptr;
|
||||
|
||||
GateTableModel *table_model = gateTableModel(arc, dcalc_ap);
|
||||
GateTableModel *table_model = arc->gateTableModel(dcalc_ap);
|
||||
if (table_model && parasitic) {
|
||||
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
|
||||
parasitics_->piModel(parasitic, c2_, rpi_, c1_);
|
||||
|
|
@ -95,25 +96,25 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
bool vdd_exists;
|
||||
LibertyCell *drvr_cell = arc->to()->libertyCell();
|
||||
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
|
||||
const RiseFall *rf = arc->toEdge()->asRiseFall();
|
||||
drvr_rf_ = arc->toEdge()->asRiseFall();
|
||||
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
|
||||
if (!vdd_exists)
|
||||
report_->error(1700, "VDD not defined in library %s", drvr_library->name());
|
||||
vth_ = drvr_library->outputThreshold(rf) * vdd_;
|
||||
vl_ = drvr_library->slewLowerThreshold(rf) * vdd_;
|
||||
vh_ = drvr_library->slewUpperThreshold(rf) * vdd_;
|
||||
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
|
||||
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
|
||||
vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
|
||||
|
||||
drvr_cell->ensureVoltageWaveforms();
|
||||
drvr_cell->ensureVoltageWaveforms(dcalc_ap);
|
||||
in_slew_ = delayAsFloat(in_slew);
|
||||
output_waveforms_ = output_waveforms;
|
||||
ref_time_ = output_waveforms_->referenceTime(in_slew_);
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
|
||||
drvr_cell->name(),
|
||||
rf->asString());
|
||||
drvr_rf_->asString());
|
||||
ArcDelay gate_delay;
|
||||
Slew drvr_slew;
|
||||
gateDelaySlew(drvr_library, rf, gate_delay, drvr_slew);
|
||||
return makeResult(drvr_library, rf, gate_delay, drvr_slew, load_pin_index_map);
|
||||
gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew);
|
||||
return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map);
|
||||
}
|
||||
}
|
||||
return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
|
||||
|
|
@ -312,9 +313,7 @@ CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library,
|
|||
dcalc_result.setGateDelay(gate_delay);
|
||||
dcalc_result.setDrvrSlew(drvr_slew);
|
||||
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
const Pin *load_pin = load_pin_index.first;
|
||||
size_t load_idx = load_pin_index.second;
|
||||
for (const auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
ArcDelay wire_delay;
|
||||
Slew load_slew;
|
||||
loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, load_slew);
|
||||
|
|
@ -466,69 +465,75 @@ CcsCeffDelayCalc::findVlTime(double v,
|
|||
|
||||
// Waveform accessors for swig/tcl.
|
||||
|
||||
Table1
|
||||
CcsCeffDelayCalc::drvrWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max)
|
||||
void
|
||||
CcsCeffDelayCalc::watchPin(const Pin *pin)
|
||||
{
|
||||
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
|
||||
drvr_rf, corner, min_max);
|
||||
if (dcalc_success)
|
||||
return drvrWaveform(in_slew_, drvr_rf);
|
||||
watch_pin_values_[pin] = FloatSeq();
|
||||
}
|
||||
|
||||
void
|
||||
CcsCeffDelayCalc::clearWatchPins()
|
||||
{
|
||||
watch_pin_values_.clear();
|
||||
}
|
||||
|
||||
PinSeq
|
||||
CcsCeffDelayCalc::watchPins() const
|
||||
{
|
||||
PinSeq pins;
|
||||
for (const auto& [pin, values] : watch_pin_values_)
|
||||
pins.push_back(pin);
|
||||
return pins;
|
||||
}
|
||||
|
||||
Waveform
|
||||
CcsCeffDelayCalc::watchWaveform(const Pin *pin)
|
||||
{
|
||||
if (pin == drvr_pin_)
|
||||
return drvrWaveform();
|
||||
else
|
||||
return loadWaveform(pin);
|
||||
}
|
||||
|
||||
Waveform
|
||||
CcsCeffDelayCalc::drvrWaveform()
|
||||
{
|
||||
if (output_waveforms_) {
|
||||
// Stitch together the ccs waveforms for each region.
|
||||
FloatSeq *drvr_times = new FloatSeq;
|
||||
FloatSeq *drvr_volts = new FloatSeq;
|
||||
for (size_t i = 0; i < region_count_; i++) {
|
||||
double t1 = region_begin_times_[i];
|
||||
double t2 = region_end_times_[i];
|
||||
size_t time_steps = 10;
|
||||
double time_step = (t2 - t1) / time_steps;
|
||||
double time_offset = region_time_offsets_[i];
|
||||
for (size_t s = 0; s <= time_steps; s++) {
|
||||
double t = t1 + s * time_step;
|
||||
drvr_times->push_back(t - time_offset);
|
||||
double v = output_waveforms_->timeVoltage(in_slew_, region_ceff_[i], t);
|
||||
if (drvr_rf_ == RiseFall::fall())
|
||||
v = vdd_ - v;
|
||||
drvr_volts->push_back(v);
|
||||
}
|
||||
}
|
||||
TableAxisPtr drvr_time_axis = make_shared<TableAxis>(TableAxisVariable::time,
|
||||
drvr_times);
|
||||
Table1 drvr_table(drvr_volts, drvr_time_axis);
|
||||
return drvr_table;
|
||||
}
|
||||
else
|
||||
return Table1();
|
||||
}
|
||||
|
||||
Table1
|
||||
CcsCeffDelayCalc::drvrWaveform(const Slew &in_slew,
|
||||
const RiseFall *drvr_rf)
|
||||
Waveform
|
||||
CcsCeffDelayCalc::loadWaveform(const Pin *load_pin)
|
||||
{
|
||||
// Stitch together the ccs waveforms for each region.
|
||||
FloatSeq *drvr_times = new FloatSeq;
|
||||
FloatSeq *drvr_volts = new FloatSeq;
|
||||
for (size_t i = 0; i < region_count_; i++) {
|
||||
double t1 = region_begin_times_[i];
|
||||
double t2 = region_end_times_[i];
|
||||
size_t time_steps = 10;
|
||||
double time_step = (t2 - t1) / time_steps;
|
||||
double time_offset = region_time_offsets_[i];
|
||||
for (size_t s = 0; s <= time_steps; s++) {
|
||||
double t = t1 + s * time_step;
|
||||
drvr_times->push_back(t - time_offset);
|
||||
double v = output_waveforms_->timeVoltage(delayAsFloat(in_slew),
|
||||
region_ceff_[i], t);
|
||||
if (drvr_rf == RiseFall::fall())
|
||||
v = vdd_ - v;
|
||||
drvr_volts->push_back(v);
|
||||
}
|
||||
}
|
||||
TableAxisPtr drvr_time_axis = make_shared<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_) {
|
||||
if (output_waveforms_) {
|
||||
bool elmore_exists = false;
|
||||
float elmore = 0.0;
|
||||
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
|
||||
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
|
||||
drvr_rf, corner, min_max);
|
||||
if (dcalc_success
|
||||
&& elmore_exists) {
|
||||
if (elmore_exists) {
|
||||
FloatSeq *load_times = new FloatSeq;
|
||||
FloatSeq *load_volts = new FloatSeq;
|
||||
double t_vh = findVlTime(vh_, elmore);
|
||||
|
|
@ -540,7 +545,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *in_pin,
|
|||
|
||||
double ignore;
|
||||
vl(t, elmore, v, ignore);
|
||||
double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
|
||||
double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v;
|
||||
load_volts->push_back(v1);
|
||||
}
|
||||
TableAxisPtr load_time_axis = make_shared<TableAxis>(TableAxisVariable::time,
|
||||
|
|
@ -552,7 +557,7 @@ CcsCeffDelayCalc::loadWaveform(const Pin *in_pin,
|
|||
return Table1();
|
||||
}
|
||||
|
||||
Table1
|
||||
Waveform
|
||||
CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
|
|
|
|||
|
|
@ -23,16 +23,20 @@ namespace sta {
|
|||
|
||||
using std::vector;
|
||||
|
||||
typedef map<const Pin*, FloatSeq, PinIdLess> WatchPinValuesMap;
|
||||
|
||||
ArcDelayCalc *
|
||||
makeCcsCeffDelayCalc(StaState *sta);
|
||||
|
||||
class CcsCeffDelayCalc : public LumpedCapDelayCalc, public ArcDcalcWaveforms
|
||||
class CcsCeffDelayCalc : public LumpedCapDelayCalc,
|
||||
public ArcDcalcWaveforms
|
||||
{
|
||||
public:
|
||||
CcsCeffDelayCalc(StaState *sta);
|
||||
virtual ~CcsCeffDelayCalc();
|
||||
ArcDelayCalc *copy() override;
|
||||
|
||||
const char *name() const override { return "ccs_ceff"; }
|
||||
bool reduceSupported() const override { return true; }
|
||||
ArcDcalcResult gateDelay(const Pin *drvr_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
|
|
@ -49,26 +53,11 @@ public:
|
|||
const DcalcAnalysisPt *dcalc_ap,
|
||||
int digits) override;
|
||||
|
||||
Table1 drvrWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
Table1 loadWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
Table1 drvrRampWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
// Record waveform for drvr/load pin.
|
||||
void watchPin(const Pin *pin) override;
|
||||
void clearWatchPins() override;
|
||||
PinSeq watchPins() const override;
|
||||
Waveform watchWaveform(const Pin *pin) override;
|
||||
|
||||
protected:
|
||||
typedef vector<double> Region;
|
||||
|
|
@ -99,14 +88,23 @@ protected:
|
|||
// Return values.
|
||||
ArcDelay &delay,
|
||||
Slew &slew);
|
||||
double findVlTime(double v,
|
||||
double elmore);
|
||||
bool makeWaveformPreamble(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
double findVlTime(double v,
|
||||
double elmore);
|
||||
Waveform drvrWaveform();
|
||||
Waveform loadWaveform(const Pin *load_pin);
|
||||
Waveform drvrRampWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
void vl(double t,
|
||||
double elmore,
|
||||
// Return values.
|
||||
|
|
@ -114,11 +112,10 @@ protected:
|
|||
double &dvl_dt);
|
||||
double vl(double t,
|
||||
double elmore);
|
||||
Table1 drvrWaveform(const Slew &in_slew,
|
||||
const RiseFall *drvr_rf);
|
||||
void fail(const char *reason);
|
||||
|
||||
const Pin *drvr_pin_;
|
||||
const RiseFall *drvr_rf_;
|
||||
double in_slew_;
|
||||
double load_cap_;
|
||||
const Parasitic *parasitic_;
|
||||
|
|
@ -148,6 +145,8 @@ protected:
|
|||
Region region_ramp_times_;
|
||||
Region region_ramp_slopes_;
|
||||
bool vl_fail_;
|
||||
// Waveform recording.
|
||||
WatchPinValuesMap watch_pin_values_;
|
||||
|
||||
const Unit *capacitance_unit_;
|
||||
// Delay calculator to use when ccs waveforms are missing from liberty.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -23,7 +23,6 @@
|
|||
#include "DmpDelayCalc.hh"
|
||||
#include "ArnoldiDelayCalc.hh"
|
||||
#include "CcsCeffDelayCalc.hh"
|
||||
#include "CcsSimDelayCalc.hh"
|
||||
#include "PrimaDelayCalc.hh"
|
||||
|
||||
namespace sta {
|
||||
|
|
@ -41,7 +40,6 @@ registerDelayCalcs()
|
|||
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
|
||||
registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
|
||||
registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc);
|
||||
registerDelayCalc("ccs_sim", makeCcsSimDelayCalc);
|
||||
registerDelayCalc("prima", makePrimaDelayCalc);
|
||||
}
|
||||
|
||||
|
|
@ -82,10 +80,8 @@ StringSeq
|
|||
delayCalcNames()
|
||||
{
|
||||
StringSeq names;
|
||||
for (auto name_dcalc : *delay_calcs) {
|
||||
const char *name = name_dcalc.first;
|
||||
for (const auto [name, make_dcalc] : *delay_calcs)
|
||||
names.push_back(name);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
%module dcalc
|
||||
|
||||
%{
|
||||
|
||||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2024, Parallax Software, Inc.
|
||||
//
|
||||
|
|
@ -18,10 +14,15 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "Sta.hh"
|
||||
%module dcalc
|
||||
|
||||
%{
|
||||
|
||||
#include "DelayCalc.hh"
|
||||
#include "ArcDelayCalc.hh"
|
||||
#include "dcalc/ArcDcalcWaveforms.hh"
|
||||
#include "dcalc/PrimaDelayCalc.hh"
|
||||
#include "Sta.hh"
|
||||
|
||||
%}
|
||||
|
||||
|
|
@ -62,82 +63,9 @@ report_delay_calc_cmd(Edge *edge,
|
|||
return Sta::sta()->reportDelayCalc(edge, arc, corner, min_max, digits);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
Table1
|
||||
ccs_input_waveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
cmdLinkedNetwork();
|
||||
Sta *sta = Sta::sta();
|
||||
ArcDcalcWaveforms *arc_dcalc = dynamic_cast<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
|
||||
set_prima_reduce_order(size_t order)
|
||||
{
|
||||
cmdLinkedNetwork();
|
||||
Sta *sta = Sta::sta();
|
||||
PrimaDelayCalc *dcalc = dynamic_cast<PrimaDelayCalc*>(sta->arcDelayCalc());
|
||||
if (dcalc) {
|
||||
|
|
@ -146,4 +74,18 @@ set_prima_reduce_order(size_t order)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
find_delays()
|
||||
{
|
||||
cmdLinkedNetwork();
|
||||
Sta::sta()->findDelays();
|
||||
}
|
||||
|
||||
void
|
||||
delays_invalid()
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
sta->delaysInvalid();
|
||||
}
|
||||
|
||||
%} // inline
|
||||
|
|
|
|||
|
|
@ -148,17 +148,17 @@ proc set_assigned_delay { args } {
|
|||
if [info exists keys(-from)] {
|
||||
set from_pins [get_port_pins_error "from_pins" $keys(-from)]
|
||||
} else {
|
||||
sta_error 181 ""set_assigned_delay" missing -from argument."
|
||||
sta_error 181 "set_assigned_delay missing -from argument."
|
||||
}
|
||||
if [info exists keys(-to)] {
|
||||
set to_pins [get_port_pins_error "to_pins" $keys(-to)]
|
||||
} else {
|
||||
sta_error 182 ""set_assigned_delay" missing -to argument."
|
||||
sta_error 182 "set_assigned_delay missing -to argument."
|
||||
}
|
||||
|
||||
set delay [lindex $args 0]
|
||||
if {![string is double $delay]} {
|
||||
sta_error 183 ""set_assigned_delay" delay is not a float."
|
||||
sta_error 183 "set_assigned_delay delay is not a float."
|
||||
}
|
||||
set delay [time_ui_sta $delay]
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ proc set_assigned_check { args } {
|
|||
}
|
||||
set check_value [lindex $args 0]
|
||||
if { ![string is double $check_value] } {
|
||||
sta_error 192 ""set_assigned_check" check_value is not a float."
|
||||
sta_error 192 "set_assigned_check check_value is not a float."
|
||||
}
|
||||
set check_value [time_ui_sta $check_value]
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,11 @@
|
|||
#include "TableModel.hh"
|
||||
#include "Network.hh"
|
||||
#include "Parasitics.hh"
|
||||
#include "Graph.hh"
|
||||
#include "Sdc.hh"
|
||||
#include "Corner.hh"
|
||||
#include "DcalcAnalysisPt.hh"
|
||||
#include "GraphDelayCalc.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -64,36 +66,6 @@ DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network,
|
|||
delete pin_iter;
|
||||
}
|
||||
|
||||
TimingModel *
|
||||
DelayCalcBase::model(const TimingArc *arc,
|
||||
const DcalcAnalysisPt *dcalc_ap) const
|
||||
{
|
||||
const OperatingConditions *op_cond = dcalc_ap->operatingConditions();
|
||||
const TimingArc *corner_arc = arc->cornerArc(dcalc_ap->libertyIndex());
|
||||
return corner_arc->model(op_cond);
|
||||
}
|
||||
|
||||
GateTimingModel *
|
||||
DelayCalcBase::gateModel(const TimingArc *arc,
|
||||
const DcalcAnalysisPt *dcalc_ap) const
|
||||
{
|
||||
return dynamic_cast<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
|
||||
DelayCalcBase::finishDrvrPin()
|
||||
{
|
||||
|
|
@ -114,10 +86,16 @@ DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin,
|
|||
{
|
||||
|
||||
LibertyLibrary *load_library = thresholdLibrary(load_pin);
|
||||
float vth = load_library->inputThreshold(rf);
|
||||
float vl = load_library->slewLowerThreshold(rf);
|
||||
float vh = load_library->slewUpperThreshold(rf);
|
||||
float slew_derate = load_library->slewDerateFromLibrary();
|
||||
float vth = 0.5;
|
||||
float vl = 0.2;
|
||||
float vh = 0.8;
|
||||
float slew_derate = 1.0;
|
||||
if (load_library) {
|
||||
vth = load_library->inputThreshold(rf);
|
||||
vl = load_library->slewLowerThreshold(rf);
|
||||
vh = load_library->slewUpperThreshold(rf);
|
||||
slew_derate = load_library->slewDerateFromLibrary();
|
||||
}
|
||||
wire_delay = -elmore * log(1.0 - vth);
|
||||
load_slew = drvr_slew + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate;
|
||||
load_slew = drvr_slew + elmore * log((1.0 - vl) / (1.0 - vh)) / slew_derate;
|
||||
|
|
@ -176,7 +154,7 @@ DelayCalcBase::checkDelay(const Pin *check_pin,
|
|||
float related_out_cap,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
CheckTimingModel *model = checkModel(arc, dcalc_ap);
|
||||
CheckTimingModel *model = arc->checkModel(dcalc_ap);
|
||||
if (model) {
|
||||
float from_slew1 = delayAsFloat(from_slew);
|
||||
float to_slew1 = delayAsFloat(to_slew);
|
||||
|
|
@ -197,7 +175,7 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin,
|
|||
const DcalcAnalysisPt *dcalc_ap,
|
||||
int digits)
|
||||
{
|
||||
CheckTimingModel *model = checkModel(arc, dcalc_ap);
|
||||
CheckTimingModel *model = arc->checkModel(dcalc_ap);
|
||||
if (model) {
|
||||
float from_slew1 = delayAsFloat(from_slew);
|
||||
float to_slew1 = delayAsFloat(to_slew);
|
||||
|
|
@ -219,4 +197,33 @@ DelayCalcBase::pinPvt(const Pin *pin,
|
|||
return pvt;
|
||||
}
|
||||
|
||||
void
|
||||
DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArg &gate,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
const Pin *drvr_pin = gate.drvrPin();
|
||||
if (drvr_pin) {
|
||||
const Parasitic *parasitic;
|
||||
float load_cap;
|
||||
graph_delay_calc_->parasiticLoad(drvr_pin, gate.drvrEdge(), dcalc_ap,
|
||||
nullptr, this, load_cap,
|
||||
parasitic);
|
||||
gate.setLoadCap(load_cap);
|
||||
gate.setParasitic(parasitic);
|
||||
const Pin *in_pin = gate.inPin();
|
||||
const Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
|
||||
const Slew &in_slew = graph_delay_calc_->edgeFromSlew(in_vertex, gate.inEdge(),
|
||||
gate.edge(), dcalc_ap);
|
||||
gate.setInSlew(in_slew);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DelayCalcBase::setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
for (ArcDcalcArg &gate : gates)
|
||||
setDcalcArgParasiticSlew(gate, dcalc_ap);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace sta {
|
|||
|
||||
class GateTableModel;
|
||||
|
||||
// ArcDelayCalc helper functions.
|
||||
class DelayCalcBase : public ArcDelayCalc
|
||||
{
|
||||
public:
|
||||
|
|
@ -32,7 +33,10 @@ public:
|
|||
const Net *net,
|
||||
const Corner *corner,
|
||||
const MinMaxAll *min_max) override;
|
||||
|
||||
void setDcalcArgParasiticSlew(ArcDcalcArg &gate,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
ArcDelay checkDelay(const Pin *check_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &from_slew,
|
||||
|
|
@ -50,14 +54,6 @@ public:
|
|||
int digits) override;
|
||||
|
||||
protected:
|
||||
GateTimingModel *gateModel(const TimingArc *arc,
|
||||
const DcalcAnalysisPt *dcalc_ap) const;
|
||||
GateTableModel *gateTableModel(const TimingArc *arc,
|
||||
const DcalcAnalysisPt *dcalc_ap) const;
|
||||
CheckTimingModel *checkModel(const TimingArc *arc,
|
||||
const DcalcAnalysisPt *dcalc_ap) const;
|
||||
TimingModel *model(const TimingArc *arc,
|
||||
const DcalcAnalysisPt *dcalc_ap) const;
|
||||
// Find the liberty library to use for logic/slew thresholds.
|
||||
LibertyLibrary *thresholdLibrary(const Pin *load_pin);
|
||||
// Adjust load_delay and load_slew from driver thresholds to load thresholds.
|
||||
|
|
|
|||
|
|
@ -1501,7 +1501,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
const LibertyCell *drvr_cell = arc->from()->libertyCell();
|
||||
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
|
||||
|
||||
GateTableModel *table_model = gateTableModel(arc, dcalc_ap);
|
||||
GateTableModel *table_model = arc->gateTableModel(dcalc_ap);
|
||||
if (table_model && parasitic) {
|
||||
float in_slew1 = delayAsFloat(in_slew);
|
||||
float c2, rpi, c1;
|
||||
|
|
@ -1516,9 +1516,7 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
dcalc_result.setGateDelay(gate_delay);
|
||||
dcalc_result.setDrvrSlew(drvr_slew);
|
||||
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
const Pin *load_pin = load_pin_index.first;
|
||||
size_t load_idx = load_pin_index.second;
|
||||
for (const auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
ArcDelay wire_delay;
|
||||
Slew load_slew;
|
||||
loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic,
|
||||
|
|
@ -1595,7 +1593,7 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
|
|||
int digits)
|
||||
{
|
||||
gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map, dcalc_ap);
|
||||
GateTimingModel *model = gateModel(arc, dcalc_ap);
|
||||
GateTableModel *model = arc->gateTableModel(dcalc_ap);
|
||||
float c_eff = 0.0;
|
||||
string result;
|
||||
if (parasitic && dmp_alg_) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class DmpCeffDelayCalc : public LumpedCapDelayCalc
|
|||
public:
|
||||
DmpCeffDelayCalc(StaState *sta);
|
||||
virtual ~DmpCeffDelayCalc();
|
||||
bool reduceSupported() const override { return true; }
|
||||
ArcDcalcResult gateDelay(const Pin *drvr_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ class DmpCeffElmoreDelayCalc : public DmpCeffDelayCalc
|
|||
public:
|
||||
DmpCeffElmoreDelayCalc(StaState *sta);
|
||||
ArcDelayCalc *copy() override;
|
||||
const char *name() const override { return "dmp_ceff_elmore"; }
|
||||
ArcDcalcResult inputPortDelay(const Pin *port_pin,
|
||||
float in_slew,
|
||||
const RiseFall *rf,
|
||||
|
|
@ -81,9 +82,7 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *,
|
|||
{
|
||||
ArcDcalcResult dcalc_result(load_pin_index_map.size());
|
||||
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
const Pin *load_pin = load_pin_index.first;
|
||||
size_t load_idx = load_pin_index.second;
|
||||
for (auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
ArcDelay wire_delay = 0.0;
|
||||
Slew load_slew = in_slew;
|
||||
bool elmore_exists = false;
|
||||
|
|
@ -130,6 +129,7 @@ class DmpCeffTwoPoleDelayCalc : public DmpCeffDelayCalc
|
|||
public:
|
||||
DmpCeffTwoPoleDelayCalc(StaState *sta);
|
||||
ArcDelayCalc *copy() override;
|
||||
const char *name() const override { return "dmp_ceff_two_pole"; }
|
||||
Parasitic *findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
|
|
@ -257,9 +257,7 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *,
|
|||
ArcDelay wire_delay = 0.0;
|
||||
Slew load_slew = in_slew;
|
||||
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
const Pin *load_pin = load_pin_index.first;
|
||||
size_t load_idx = load_pin_index.second;
|
||||
for (const auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
if (parasitics_->isPiPoleResidue(parasitic)) {
|
||||
const Parasitic *pole_residue = parasitics_->findPoleResidue(parasitic, load_pin);
|
||||
if (pole_residue) {
|
||||
|
|
|
|||
|
|
@ -366,12 +366,12 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex,
|
|||
float drive_res;
|
||||
drive->driveResistance(rf, cnst_min_max, drive_res, exists);
|
||||
const Parasitic *parasitic;
|
||||
float cap;
|
||||
float load_cap;
|
||||
parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc,
|
||||
cap, parasitic);
|
||||
load_cap, parasitic);
|
||||
if (exists) {
|
||||
drive_delay = cap * drive_res;
|
||||
slew = cap * drive_res;
|
||||
drive_delay = load_cap * drive_res;
|
||||
slew = load_cap * drive_res;
|
||||
}
|
||||
const MinMax *slew_min_max = dcalc_ap->slewMinMax();
|
||||
if (!drvr_vertex->slewAnnotated(rf, slew_min_max))
|
||||
|
|
@ -796,7 +796,7 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex,
|
|||
else
|
||||
initWireDelays(drvr_vertex);
|
||||
bool delay_changed = false;
|
||||
bool has_delays = false;
|
||||
array<bool, RiseFall::index_count> delay_exists = {false, false};
|
||||
VertexInEdgeIterator edge_iter(drvr_vertex, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *edge = edge_iter.next();
|
||||
|
|
@ -804,14 +804,14 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex,
|
|||
// Don't let disabled edges set slews that influence downstream delays.
|
||||
if (search_pred_->searchFrom(from_vertex)
|
||||
&& search_pred_->searchThru(edge)
|
||||
&& !edge->role()->isLatchDtoQ()) {
|
||||
&& !edge->role()->isLatchDtoQ())
|
||||
delay_changed |= findDriverEdgeDelays(drvr_vertex, multi_drvr, edge,
|
||||
arc_delay_calc);
|
||||
has_delays = true;
|
||||
}
|
||||
arc_delay_calc, delay_exists);
|
||||
}
|
||||
for (auto rf : RiseFall::range()) {
|
||||
if (!delay_exists[rf->index()])
|
||||
zeroSlewAndWireDelays(drvr_vertex, rf);
|
||||
}
|
||||
if (!has_delays)
|
||||
zeroSlewAndWireDelays(drvr_vertex);
|
||||
if (delay_changed && observer_)
|
||||
observer_->delayChangedTo(drvr_vertex);
|
||||
return delay_changed;
|
||||
|
|
@ -840,8 +840,9 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge)
|
|||
Instance *drvr_inst = network_->instance(drvr_pin);
|
||||
debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s",
|
||||
sdc_network_->pathName(drvr_inst));
|
||||
array<bool, RiseFall::index_count> delay_exists = {false, false};
|
||||
bool delay_changed = findDriverEdgeDelays(drvr_vertex, nullptr, edge,
|
||||
arc_delay_calc_);
|
||||
arc_delay_calc_, delay_exists);
|
||||
if (delay_changed && observer_)
|
||||
observer_->delayChangedTo(drvr_vertex);
|
||||
}
|
||||
|
|
@ -850,17 +851,20 @@ bool
|
|||
GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex,
|
||||
const MultiDrvrNet *multi_drvr,
|
||||
Edge *edge,
|
||||
ArcDelayCalc *arc_delay_calc)
|
||||
ArcDelayCalc *arc_delay_calc,
|
||||
array<bool, RiseFall::index_count> &delay_exists)
|
||||
{
|
||||
Vertex *from_vertex = edge->from(graph_);
|
||||
const TimingArcSet *arc_set = edge->timingArcSet();
|
||||
bool delay_changed = false;
|
||||
LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex);
|
||||
for (auto dcalc_ap : corners_->dcalcAnalysisPts()) {
|
||||
for (const TimingArc *arc : arc_set->arcs())
|
||||
for (const TimingArc *arc : arc_set->arcs()) {
|
||||
delay_changed |= findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc,
|
||||
load_pin_index_map, dcalc_ap,
|
||||
arc_delay_calc);
|
||||
delay_exists[arc->toEdge()->asRiseFall()->index()] = true;
|
||||
}
|
||||
}
|
||||
if (delay_changed && observer_) {
|
||||
observer_->delayChangedFrom(from_vertex);
|
||||
|
|
@ -908,8 +912,7 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex,
|
|||
edge, arc, dcalc_ap,
|
||||
arc_delay_calc);
|
||||
ArcDcalcResultSeq dcalc_results =
|
||||
arc_delay_calc->gateDelays(dcalc_args, load_cap, load_pin_index_map,
|
||||
dcalc_ap);
|
||||
arc_delay_calc->gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
|
||||
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
|
||||
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
|
||||
ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx];
|
||||
|
|
@ -959,9 +962,12 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex,
|
|||
const RiseFall *drvr_rf = arc1->toEdge()->asRiseFall();
|
||||
const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, dcalc_ap);
|
||||
const Pin *drvr_pin1 = drvr_vertex1->pin();
|
||||
Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin1, drvr_rf,
|
||||
dcalc_ap);
|
||||
dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, parasitic);
|
||||
float load_cap;
|
||||
const Parasitic *parasitic;
|
||||
parasiticLoad(drvr_pin1, drvr_rf, dcalc_ap, multi_drvr, arc_delay_calc,
|
||||
load_cap, parasitic);
|
||||
dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew,
|
||||
load_cap, parasitic);
|
||||
}
|
||||
}
|
||||
return dcalc_args;
|
||||
|
|
@ -1057,7 +1063,7 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge,
|
|||
// Merge slews.
|
||||
const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index);
|
||||
const MinMax *slew_min_max = dcalc_ap->slewMinMax();
|
||||
if (delayGreater(gate_slew, drvr_slew, dcalc_ap->slewMinMax(), this)
|
||||
if (delayGreater(gate_slew, drvr_slew, slew_min_max, this)
|
||||
&& !drvr_vertex->slewAnnotated(drvr_rf, slew_min_max)
|
||||
&& !edge->role()->isLatchDtoQ())
|
||||
graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew);
|
||||
|
|
@ -1328,30 +1334,29 @@ GraphDelayCalc::initSlew(Vertex *vertex)
|
|||
}
|
||||
|
||||
void
|
||||
GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex)
|
||||
GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex,
|
||||
const RiseFall *rf)
|
||||
{
|
||||
for (auto dcalc_ap : corners_->dcalcAnalysisPts()) {
|
||||
DcalcAPIndex ap_index = dcalc_ap->index();
|
||||
const MinMax *slew_min_max = dcalc_ap->slewMinMax();
|
||||
for (auto rf : RiseFall::range()) {
|
||||
// Init drvr slew.
|
||||
if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) {
|
||||
DcalcAPIndex ap_index = dcalc_ap->index();
|
||||
graph_->setSlew(drvr_vertex, rf, ap_index, slew_min_max->initValue());
|
||||
}
|
||||
// Init drvr slew.
|
||||
if (!drvr_vertex->slewAnnotated(rf, slew_min_max)) {
|
||||
DcalcAPIndex ap_index = dcalc_ap->index();
|
||||
graph_->setSlew(drvr_vertex, rf, ap_index, slew_min_max->initValue());
|
||||
}
|
||||
|
||||
// Init wire delays and slews.
|
||||
VertexOutEdgeIterator edge_iter(drvr_vertex, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *wire_edge = edge_iter.next();
|
||||
if (wire_edge->isWire()) {
|
||||
Vertex *load_vertex = wire_edge->to(graph_);
|
||||
if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index))
|
||||
graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0);
|
||||
// Init load vertex slew.
|
||||
if (!load_vertex->slewAnnotated(rf, slew_min_max))
|
||||
graph_->setSlew(load_vertex, rf, ap_index, 0.0);
|
||||
}
|
||||
// Init wire delays and slews.
|
||||
VertexOutEdgeIterator edge_iter(drvr_vertex, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *wire_edge = edge_iter.next();
|
||||
if (wire_edge->isWire()) {
|
||||
Vertex *load_vertex = wire_edge->to(graph_);
|
||||
if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index))
|
||||
graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0);
|
||||
// Init load vertex slew.
|
||||
if (!load_vertex->slewAnnotated(rf, slew_min_max))
|
||||
graph_->setSlew(load_vertex, rf, ap_index, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1377,14 +1382,23 @@ GraphDelayCalc::initWireDelays(Vertex *drvr_vertex)
|
|||
}
|
||||
}
|
||||
|
||||
// Use clock slew for register/latch clk->q edges.
|
||||
Slew
|
||||
GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex,
|
||||
const RiseFall *from_rf,
|
||||
const Edge *edge,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
const TimingRole *role = edge->role();
|
||||
return edgeFromSlew(from_vertex, from_rf, edge->role(), dcalc_ap);
|
||||
}
|
||||
|
||||
// Use clock slew for register/latch clk->q edges.
|
||||
Slew
|
||||
GraphDelayCalc::edgeFromSlew(const Vertex *from_vertex,
|
||||
const RiseFall *from_rf,
|
||||
const TimingRole *role,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
|
||||
if (role->genericRole() == TimingRole::regClkToQ()
|
||||
&& clk_network_->isIdealClock(from_vertex->pin()))
|
||||
return clk_network_->idealClkSlew(from_vertex->pin(), from_rf,
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
GateTimingModel *model = gateModel(arc, dcalc_ap);
|
||||
GateTimingModel *model = arc->gateModel(dcalc_ap);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" in_slew = %s load_cap = %s lumped",
|
||||
delayAsString(in_slew, this),
|
||||
|
|
@ -155,9 +155,7 @@ LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library,
|
|||
dcalc_result.setGateDelay(gate_delay);
|
||||
dcalc_result.setDrvrSlew(drvr_slew);
|
||||
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
const Pin *load_pin = load_pin_index.first;
|
||||
size_t load_idx = load_pin_index.second;
|
||||
for (const auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
ArcDelay wire_delay = 0.0;
|
||||
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, drvr_slew);
|
||||
dcalc_result.setWireDelay(load_idx, wire_delay);
|
||||
|
|
@ -176,7 +174,7 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin,
|
|||
const DcalcAnalysisPt *dcalc_ap,
|
||||
int digits)
|
||||
{
|
||||
GateTimingModel *model = gateModel(arc, dcalc_ap);
|
||||
GateTimingModel *model = arc->gateModel(dcalc_ap);
|
||||
if (model) {
|
||||
float in_slew1 = delayAsFloat(in_slew);
|
||||
return model->reportGateDelay(pinPvt(check_pin, dcalc_ap), in_slew1, load_cap,
|
||||
|
|
|
|||
|
|
@ -27,9 +27,11 @@ class LumpedCapDelayCalc : public ParallelDelayCalc
|
|||
public:
|
||||
LumpedCapDelayCalc(StaState *sta);
|
||||
ArcDelayCalc *copy() override;
|
||||
const char *name() const override { return "lumped_cap"; }
|
||||
Parasitic *findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
bool reduceSupported() const override { return true; }
|
||||
Parasitic *reduceParasitic(const Parasitic *parasitic_network,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
|
|
|
|||
|
|
@ -33,26 +33,24 @@ ParallelDelayCalc::ParallelDelayCalc(StaState *sta):
|
|||
|
||||
ArcDcalcResultSeq
|
||||
ParallelDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
if (dcalc_args.size() == 1) {
|
||||
ArcDcalcArg &dcalc_arg = dcalc_args[0];
|
||||
ArcDcalcResult dcalc_result = gateDelay(dcalc_arg.drvrPin(), dcalc_arg.arc(),
|
||||
dcalc_arg.inSlew(),
|
||||
load_cap, dcalc_arg.parasitic(),
|
||||
dcalc_arg.inSlew(), dcalc_arg.loadCap(),
|
||||
dcalc_arg.parasitic(),
|
||||
load_pin_index_map, dcalc_ap);
|
||||
ArcDcalcResultSeq dcalc_results;
|
||||
dcalc_results.push_back(dcalc_result);
|
||||
return dcalc_results;
|
||||
}
|
||||
return gateDelaysParallel(dcalc_args, load_cap, load_pin_index_map, dcalc_ap);
|
||||
return gateDelaysParallel(dcalc_args, load_pin_index_map, dcalc_ap);
|
||||
}
|
||||
|
||||
ArcDcalcResultSeq
|
||||
ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
|
|
@ -74,7 +72,7 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
|
|||
ArcDelay intrinsic_delay = intrinsic_result.gateDelay();
|
||||
intrinsic_delays[drvr_idx] = intrinsic_result.gateDelay();
|
||||
|
||||
ArcDcalcResult gate_result = gateDelay(drvr_pin, arc, in_slew, load_cap,
|
||||
ArcDcalcResult gate_result = gateDelay(drvr_pin, arc, in_slew, dcalc_arg.loadCap(),
|
||||
dcalc_arg.parasitic(),
|
||||
load_pin_index_map, dcalc_ap);
|
||||
ArcDelay gate_delay = gate_result.gateDelay();
|
||||
|
|
@ -88,8 +86,7 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
|
|||
slew_sum += 1.0 / drvr_slew;
|
||||
|
||||
dcalc_result.setLoadCount(load_pin_index_map.size());
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
size_t load_idx = load_pin_index.second;
|
||||
for (const auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
dcalc_result.setWireDelay(load_idx, gate_result.wireDelay(load_idx));
|
||||
dcalc_result.setLoadSlew(load_idx, gate_result.loadSlew(load_idx));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,12 +29,10 @@ class ParallelDelayCalc : public DelayCalcBase
|
|||
public:
|
||||
ParallelDelayCalc(StaState *sta);
|
||||
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
protected:
|
||||
ArcDcalcResultSeq gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ PrimaDelayCalc::PrimaDelayCalc(StaState *sta) :
|
|||
dcalc_args_(nullptr),
|
||||
load_pin_index_map_(nullptr),
|
||||
pin_node_map_(network_),
|
||||
node_index_map_(ParasiticNodeLess(parasitics_, network_)),
|
||||
prima_order_(3),
|
||||
make_waveforms_(false),
|
||||
waveform_drvr_pin_(nullptr),
|
||||
|
|
@ -71,6 +72,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) :
|
|||
dcalc_args_(nullptr),
|
||||
load_pin_index_map_(nullptr),
|
||||
pin_node_map_(network_),
|
||||
node_index_map_(ParasiticNodeLess(parasitics_, network_)),
|
||||
prima_order_(dcalc.prima_order_),
|
||||
make_waveforms_(false),
|
||||
waveform_drvr_pin_(nullptr),
|
||||
|
|
@ -184,22 +186,19 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
ArcDcalcArgSeq dcalc_args;
|
||||
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, parasitic);
|
||||
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap,
|
||||
load_pin_index_map, dcalc_ap);
|
||||
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic);
|
||||
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, dcalc_ap);
|
||||
return dcalc_results[0];
|
||||
}
|
||||
|
||||
ArcDcalcResultSeq
|
||||
PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
dcalc_args_ = &dcalc_args;
|
||||
load_pin_index_map_ = &load_pin_index_map;
|
||||
drvr_count_ = dcalc_args.size();
|
||||
load_cap_ = load_cap;
|
||||
dcalc_ap_ = dcalc_ap;
|
||||
drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall();
|
||||
parasitic_network_ = dcalc_args[0].parasitic();
|
||||
|
|
@ -208,14 +207,14 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
|||
output_waveforms_.resize(drvr_count_);
|
||||
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
|
||||
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
|
||||
GateTableModel *table_model = gateTableModel(dcalc_arg.arc(), dcalc_ap);
|
||||
GateTableModel *table_model = dcalc_arg.arc()->gateTableModel(dcalc_ap);
|
||||
if (table_model && dcalc_arg.parasitic()) {
|
||||
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
|
||||
Slew in_slew = dcalc_arg.inSlew();
|
||||
if (output_waveforms
|
||||
// Bounds check because extrapolating waveforms does not work for shit.
|
||||
&& output_waveforms->slewAxis()->inBounds(in_slew)
|
||||
&& output_waveforms->capAxis()->inBounds(load_cap)) {
|
||||
&& output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) {
|
||||
output_waveforms_[drvr_idx] = output_waveforms;
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
|
||||
dcalc_arg.drvrCell()->name(),
|
||||
|
|
@ -226,7 +225,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
|||
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
|
||||
if (!vdd_exists)
|
||||
report_->error(1720, "VDD not defined in library %s", drvr_library->name());
|
||||
drvr_cell->ensureVoltageWaveforms();
|
||||
drvr_cell->ensureVoltageWaveforms(dcalc_ap);
|
||||
if (drvr_idx == 0) {
|
||||
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
|
||||
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
|
||||
|
|
@ -241,7 +240,7 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
|||
}
|
||||
|
||||
if (failed)
|
||||
return tableDcalcResults(load_cap);
|
||||
return tableDcalcResults();
|
||||
else {
|
||||
simulate();
|
||||
return dcalcResults();
|
||||
|
|
@ -249,17 +248,18 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
|||
}
|
||||
|
||||
ArcDcalcResultSeq
|
||||
PrimaDelayCalc::tableDcalcResults(float load_cap)
|
||||
PrimaDelayCalc::tableDcalcResults()
|
||||
{
|
||||
for (size_t drvr_idx = 0; drvr_idx < drvr_count_; drvr_idx++) {
|
||||
ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx];
|
||||
const Pin *drvr_pin = dcalc_arg.drvrPin();
|
||||
const RiseFall *rf = dcalc_arg.drvrEdge();
|
||||
const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, dcalc_ap_);
|
||||
dcalc_arg.setParasitic(parasitic);
|
||||
if (drvr_pin) {
|
||||
const RiseFall *rf = dcalc_arg.drvrEdge();
|
||||
const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, dcalc_ap_);
|
||||
dcalc_arg.setParasitic(parasitic);
|
||||
}
|
||||
}
|
||||
return table_dcalc_->gateDelays(*dcalc_args_, load_cap, *load_pin_index_map_,
|
||||
dcalc_ap_);
|
||||
return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, dcalc_ap_);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -902,7 +902,7 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin,
|
|||
const DcalcAnalysisPt *dcalc_ap,
|
||||
int digits)
|
||||
{
|
||||
GateTimingModel *model = gateModel(arc, dcalc_ap);
|
||||
GateTimingModel *model = arc->gateModel(dcalc_ap);
|
||||
if (model) {
|
||||
float in_slew1 = delayAsFloat(in_slew);
|
||||
return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap,
|
||||
|
|
@ -950,96 +950,6 @@ PrimaDelayCalc::watchWaveform(const Pin *pin)
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// Waveform accessors for swig/tcl.
|
||||
Table1
|
||||
PrimaDelayCalc::drvrWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, nullptr, corner, min_max);
|
||||
TableAxisPtr time_axis = make_shared<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
|
||||
PrimaDelayCalc::reportMatrix(const char *name,
|
||||
MatrixSd &matrix)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "Map.hh"
|
||||
#include "LumpedCapDelayCalc.hh"
|
||||
#include "ArcDcalcWaveforms.hh"
|
||||
#include "Parasitics.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ using Eigen::Index;
|
|||
using std::map;
|
||||
|
||||
typedef Map<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 SparseMatrix<double> MatrixSd;
|
||||
typedef Map<const Pin*, VectorXd, PinIdLess> PinLMap;
|
||||
|
|
@ -61,10 +62,12 @@ public:
|
|||
~PrimaDelayCalc();
|
||||
ArcDelayCalc *copy() override;
|
||||
void copyState(const StaState *sta) override;
|
||||
const char *name() const override { return "prima"; }
|
||||
void setPrimaReduceOrder(size_t order);
|
||||
Parasitic *findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
bool reduceSupported() const override { return false; }
|
||||
Parasitic *reduceParasitic(const Parasitic *parasitic_network,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
|
|
@ -83,7 +86,6 @@ public:
|
|||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
string reportGateDelay(const Pin *drvr_pin,
|
||||
|
|
@ -96,31 +98,13 @@ public:
|
|||
int digits) override;
|
||||
|
||||
// Record waveform for drvr/load pin.
|
||||
void watchPin(const Pin *pin);
|
||||
void clearWatchPins();
|
||||
PinSeq watchPins() const;
|
||||
Waveform watchWaveform(const Pin *pin);
|
||||
void watchPin(const Pin *pin) override;
|
||||
void clearWatchPins() override;
|
||||
PinSeq watchPins() const override;
|
||||
Waveform watchWaveform(const Pin *pin) override;
|
||||
|
||||
Waveform inputWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
Waveform drvrWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
Waveform loadWaveform(const Pin *in_pin,
|
||||
const RiseFall *in_rf,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *drvr_rf,
|
||||
const Pin *load_pin,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) override;
|
||||
|
||||
protected:
|
||||
ArcDcalcResultSeq tableDcalcResults(float load_cap);
|
||||
ArcDcalcResultSeq tableDcalcResults();
|
||||
void simulate();
|
||||
void simulate1(const MatrixSd &G,
|
||||
const MatrixSd &C,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,18 @@ UnitDelayCalc::reduceParasitic(const Parasitic *,
|
|||
{
|
||||
}
|
||||
|
||||
void
|
||||
UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArg &,
|
||||
const DcalcAnalysisPt *)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
UnitDelayCalc::setDcalcArgParasiticSlew(ArcDcalcArgSeq &,
|
||||
const DcalcAnalysisPt *)
|
||||
{
|
||||
}
|
||||
|
||||
ArcDcalcResult
|
||||
UnitDelayCalc::inputPortDelay(const Pin *,
|
||||
float,
|
||||
|
|
@ -87,7 +99,6 @@ UnitDelayCalc::gateDelay(const Pin *,
|
|||
|
||||
ArcDcalcResultSeq
|
||||
UnitDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
||||
float,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,9 +26,11 @@ class UnitDelayCalc : public ArcDelayCalc
|
|||
public:
|
||||
UnitDelayCalc(StaState *sta);
|
||||
ArcDelayCalc *copy() override;
|
||||
const char *name() const override { return "unit"; }
|
||||
Parasitic *findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
bool reduceSupported() const override { return false; }
|
||||
Parasitic *reduceParasitic(const Parasitic *parasitic_network,
|
||||
const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
|
|
@ -37,6 +39,10 @@ public:
|
|||
const Net *net,
|
||||
const Corner *corner,
|
||||
const MinMaxAll *min_max) override;
|
||||
void setDcalcArgParasiticSlew(ArcDcalcArg &gate,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
ArcDcalcResult inputPortDelay(const Pin *port_pin,
|
||||
float in_slew,
|
||||
const RiseFall *rf,
|
||||
|
|
@ -52,7 +58,6 @@ public:
|
|||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) override;
|
||||
ArcDelay checkDelay(const Pin *check_pin,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,26 @@
|
|||
OpenSTA Timing Analyzer Release Notes
|
||||
---------------------------------------------
|
||||
-------------------------------------
|
||||
|
||||
This file summarizes user visible changes for each release.
|
||||
|
||||
Release 2.6.0 2024/07/??
|
||||
-------------------------
|
||||
|
||||
The version of c++ used by OpenSTA is now 17.
|
||||
The Cudd BBD packager is now required.
|
||||
|
||||
The USE_TCL_READLINE option defaults to ON.
|
||||
|
||||
The variable link_make_black_boxes has been removed.
|
||||
Use the -no_black_boxes option to the link_design command.
|
||||
|
||||
link_design [-no_black_blocks] [top_cell_name]
|
||||
|
||||
The all_inputs command now supports -no_clocks to remove clocks
|
||||
inputs from the returned list.
|
||||
|
||||
all_inputs [-no_clocks]
|
||||
|
||||
Release 2.5.0 2024/01/17
|
||||
-------------------------
|
||||
|
||||
|
|
@ -24,6 +42,11 @@ timing groups.
|
|||
report_clock_skew -include_internal_latency
|
||||
report_clock_latency -include_internal_latency
|
||||
|
||||
The all_inputs command now supports the -no_clocks argument to exclude
|
||||
clocks from the list.
|
||||
|
||||
all_inputs [-no_clocks]
|
||||
|
||||
Release 2.4.0 2023/01/19
|
||||
-------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ member variable - snake case with trailing underscore (member_variable_)
|
|||
Trailing underscore prevents conflict with accessor
|
||||
member function name.
|
||||
function - lower camel case (functionName)
|
||||
variable - snake case
|
||||
comments - use capitalized sentences that end with periods
|
||||
|
||||
C++ code files should use a .cc file extension
|
||||
|
|
@ -55,7 +56,7 @@ this:
|
|||
instead, write this:
|
||||
|
||||
foo = (char *) malloc (sizeof *foo);
|
||||
if (foo == 0)
|
||||
if (foo == nullptr)
|
||||
fatal ("virtual memory exhausted");
|
||||
|
||||
|
||||
|
|
|
|||
BIN
doc/OpenSTA.odt
BIN
doc/OpenSTA.odt
Binary file not shown.
BIN
doc/OpenSTA.pdf
BIN
doc/OpenSTA.pdf
Binary file not shown.
|
|
@ -757,10 +757,8 @@ Graph::makeArcDelayTables(DcalcAPIndex ap_count)
|
|||
{
|
||||
if (have_arc_delays_) {
|
||||
arc_delays_.resize(ap_count);
|
||||
for (DcalcAPIndex i = 0; i < ap_count; i++) {
|
||||
DelayTable *table = new DelayTable();
|
||||
arc_delays_[i] = table;
|
||||
}
|
||||
for (DcalcAPIndex i = 0; i < ap_count; i++)
|
||||
arc_delays_[i] = new DelayTable();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1041,7 +1039,7 @@ Graph::setPeriodCheckAnnotation(const Pin *pin,
|
|||
float period)
|
||||
{
|
||||
if (period_check_annotations_ == nullptr)
|
||||
period_check_annotations_ = new PeriodCheckAnnotations;
|
||||
period_check_annotations_ = new PeriodCheckAnnotations(network_);
|
||||
float *periods = period_check_annotations_->findKey(pin);
|
||||
if (periods == nullptr) {
|
||||
periods = new float[ap_count_];
|
||||
|
|
@ -1057,10 +1055,8 @@ void
|
|||
Graph::removePeriodCheckAnnotations()
|
||||
{
|
||||
if (period_check_annotations_) {
|
||||
for (auto pin_floats : *period_check_annotations_) {
|
||||
float *periods = pin_floats.second;
|
||||
for (const auto [pin, periods] : *period_check_annotations_)
|
||||
delete [] periods;
|
||||
}
|
||||
delete period_check_annotations_;
|
||||
period_check_annotations_ = nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ VertexNameLess::operator()(const Vertex *vertex1,
|
|||
////////////////////////////////////////////////////////////////
|
||||
|
||||
EdgeLess::EdgeLess(const Network *network,
|
||||
Graph *graph) :
|
||||
Graph *&graph) :
|
||||
pin_less_(network),
|
||||
graph_(graph)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ class Corner;
|
|||
class Parasitic;
|
||||
class DcalcAnalysisPt;
|
||||
class MultiDrvrNet;
|
||||
class ArcDcalcArg;
|
||||
|
||||
typedef std::vector<ArcDcalcArg*> ArcDcalcArgPtrSeq;
|
||||
typedef std::vector<ArcDcalcArg> ArcDcalcArgSeq;
|
||||
|
||||
// Driver load pin -> index in driver loads.
|
||||
typedef map<const Pin *, size_t, PinIdLess> LoadPinIndexMap;
|
||||
|
|
@ -56,6 +60,7 @@ public:
|
|||
Edge *edge,
|
||||
const TimingArc *arc,
|
||||
const Slew in_slew,
|
||||
float load_cap,
|
||||
const Parasitic *parasitic);
|
||||
ArcDcalcArg(const Pin *in_pin,
|
||||
const Pin *drvr_pin,
|
||||
|
|
@ -65,6 +70,7 @@ public:
|
|||
const Pin *inPin() const { return in_pin_; }
|
||||
const RiseFall *inEdge() const;
|
||||
const Pin *drvrPin() const { return drvr_pin_; }
|
||||
Vertex *drvrVertex(const Graph *graph) const;
|
||||
LibertyCell *drvrCell() const;
|
||||
const LibertyLibrary *drvrLibrary() const;
|
||||
const RiseFall *drvrEdge() const;
|
||||
|
|
@ -74,9 +80,12 @@ public:
|
|||
Slew inSlew() const { return in_slew_; }
|
||||
float inSlewFlt() const;
|
||||
void setInSlew(Slew in_slew);
|
||||
const Parasitic *parasitic() { return parasitic_; }
|
||||
const Parasitic *parasitic() const { return parasitic_; }
|
||||
void setParasitic(const Parasitic *parasitic);
|
||||
float loadCap() const { return load_cap_; }
|
||||
void setLoadCap(float load_cap);
|
||||
float inputDelay() const { return input_delay_; }
|
||||
void setInputDelay(float input_delay);
|
||||
|
||||
protected:
|
||||
const Pin *in_pin_;
|
||||
|
|
@ -84,10 +93,21 @@ protected:
|
|||
Edge *edge_;
|
||||
const TimingArc *arc_;
|
||||
Slew in_slew_;
|
||||
float load_cap_;
|
||||
const Parasitic *parasitic_;
|
||||
float input_delay_;
|
||||
};
|
||||
|
||||
|
||||
ArcDcalcArg
|
||||
makeArcDcalcArg(const char *inst_name,
|
||||
const char *in_port_name,
|
||||
const char *in_rf_name,
|
||||
const char *drvr_port_name,
|
||||
const char *drvr_rf_name,
|
||||
const char *input_delay_str,
|
||||
const StaState *sta);
|
||||
|
||||
// Arc delay calc result.
|
||||
class ArcDcalcResult
|
||||
{
|
||||
|
|
@ -127,6 +147,9 @@ typedef vector<ArcDcalcResult> ArcDcalcResultSeq;
|
|||
// DmpCeffElmoreDelayCalc
|
||||
// DmpCeffTwoPoleDelayCalc
|
||||
// ArnoldiDelayCalc
|
||||
// CcsCeffDelayCalc
|
||||
// CcsSimfDelayCalc
|
||||
// PrimafDelayCalc
|
||||
|
||||
// Abstract class for the graph delay calculator traversal to interface
|
||||
// to a delay calculator primitive.
|
||||
|
|
@ -136,12 +159,14 @@ public:
|
|||
explicit ArcDelayCalc(StaState *sta);
|
||||
virtual ~ArcDelayCalc() {}
|
||||
virtual ArcDelayCalc *copy() = 0;
|
||||
virtual const char *name() const = 0;
|
||||
|
||||
// Find the parasitic for drvr_pin that is acceptable to the delay
|
||||
// calculator by probing parasitics_.
|
||||
virtual Parasitic *findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const DcalcAnalysisPt *dcalc_ap) = 0;
|
||||
virtual bool reduceSupported() const = 0;
|
||||
// Reduce parasitic_network to a representation acceptable to the delay calculator.
|
||||
virtual Parasitic *reduceParasitic(const Parasitic *parasitic_network,
|
||||
const Pin *drvr_pin,
|
||||
|
|
@ -154,6 +179,11 @@ public:
|
|||
const Net *net,
|
||||
const Corner *corner,
|
||||
const MinMaxAll *min_max) = 0;
|
||||
// Set the in_slew, load_cap, parasitic for gates.
|
||||
virtual void setDcalcArgParasiticSlew(ArcDcalcArg &gate,
|
||||
const DcalcAnalysisPt *dcalc_ap) = 0;
|
||||
virtual void setDcalcArgParasiticSlew(ArcDcalcArgSeq &gates,
|
||||
const DcalcAnalysisPt *dcalc_ap) = 0;
|
||||
// Find the wire delays and slews for an input port without a driving cell.
|
||||
// This call primarily initializes the load delay/slew iterator.
|
||||
virtual ArcDcalcResult inputPortDelay(const Pin *port_pin,
|
||||
|
|
@ -172,6 +202,7 @@ public:
|
|||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) = 0;
|
||||
// deprecated 2024-02-27
|
||||
virtual void gateDelay(const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
float load_cap,
|
||||
|
|
@ -185,7 +216,6 @@ public:
|
|||
|
||||
// Find gate delays and slews for parallel gates.
|
||||
virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args,
|
||||
float load_cap,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const DcalcAnalysisPt *dcalc_ap) = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,12 +21,8 @@
|
|||
#include "StaState.hh"
|
||||
#include "LibertyClass.hh"
|
||||
|
||||
#if CUDD
|
||||
#include "cudd.h"
|
||||
#else
|
||||
struct DdNode;
|
||||
struct DdManager;
|
||||
#endif
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ template <class ENUM>
|
|||
EnumNameMap<ENUM>::EnumNameMap(initializer_list<pair<const ENUM, string>> enum_names) :
|
||||
enum_map_(enum_names)
|
||||
{
|
||||
for (auto iter = enum_map_.begin(); iter != enum_map_.end(); iter++)
|
||||
name_map_[iter->second] = iter->first;
|
||||
for (const auto& [key, name] : enum_map_)
|
||||
name_map_[name] = key;
|
||||
}
|
||||
|
||||
template <class ENUM>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ public:
|
|||
int priority,
|
||||
const char *comment);
|
||||
virtual ~ExceptionPath();
|
||||
size_t id() const { return id_; }
|
||||
void setId(size_t id);
|
||||
virtual bool isFalse() const { return false; }
|
||||
virtual bool isLoop() const { return false; }
|
||||
virtual bool isMultiCycle() const { return false; }
|
||||
|
|
@ -128,6 +130,7 @@ protected:
|
|||
const MinMaxAll *min_max_;
|
||||
bool own_pts_;
|
||||
int priority_;
|
||||
size_t id_; // Unique ID assigned by Sdc.
|
||||
ExceptionState *states_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ typedef ArrayTable<Required> RequiredsTable;
|
|||
typedef ArrayTable<PathVertexRep> PrevPathsTable;
|
||||
typedef Map<const Pin*, Vertex*> PinVertexMap;
|
||||
typedef Iterator<Edge*> VertexEdgeIterator;
|
||||
typedef Map<const Pin*, float*> PeriodCheckAnnotations;
|
||||
typedef Map<const Pin*, float*, PinIdLess> PeriodCheckAnnotations;
|
||||
typedef Vector<DelayTable*> DelayTableSeq;
|
||||
typedef ObjectId EdgeId;
|
||||
typedef ObjectId ArrivalId;
|
||||
|
|
@ -352,13 +352,13 @@ protected:
|
|||
EdgeId in_edges_; // Edges to this vertex.
|
||||
EdgeId out_edges_; // Edges from this vertex.
|
||||
|
||||
// 4 bytes
|
||||
// 32 bits
|
||||
unsigned int tag_group_index_:tag_group_index_bits; // 24
|
||||
// Each bit corresponds to a different BFS queue.
|
||||
unsigned int bfs_in_queue_:int(BfsIndex::bits); // 4
|
||||
unsigned int slew_annotated_:slew_annotated_bits;
|
||||
|
||||
// 4 bytes (32 bits)
|
||||
// 32 bits
|
||||
unsigned int level_:Graph::vertex_level_bits;
|
||||
// Levelization search state.
|
||||
// LevelColor gcc barfs if this is dcl'd.
|
||||
|
|
@ -369,6 +369,8 @@ protected:
|
|||
// This flag distinguishes the driver and load vertices.
|
||||
bool is_bidirect_drvr_:1;
|
||||
bool is_reg_clk_:1;
|
||||
|
||||
// 15 bits
|
||||
bool is_disabled_constraint_:1;
|
||||
bool is_gated_clk_enable_:1;
|
||||
// Constrained by timing check edge.
|
||||
|
|
@ -379,7 +381,6 @@ protected:
|
|||
bool has_downstream_clk_pin_:1;
|
||||
bool crpr_path_pruning_disabled_:1;
|
||||
bool requireds_pruned_:1;
|
||||
|
||||
unsigned object_idx_:VertexTable::idx_bits;
|
||||
|
||||
private:
|
||||
|
|
@ -441,6 +442,7 @@ protected:
|
|||
EdgeId vertex_out_next_; // Vertex out edges doubly linked list.
|
||||
EdgeId vertex_out_prev_;
|
||||
ArcId arc_delays_;
|
||||
// 16 bits
|
||||
bool delay_annotation_is_incremental_:1;
|
||||
bool is_bidirect_inst_path_:1;
|
||||
bool is_bidirect_net_path_:1;
|
||||
|
|
|
|||
|
|
@ -37,13 +37,13 @@ class EdgeLess
|
|||
{
|
||||
public:
|
||||
EdgeLess(const Network *network,
|
||||
Graph *graph);
|
||||
Graph *&graph);
|
||||
bool operator()(const Edge *edge1,
|
||||
const Edge *edge2) const;
|
||||
|
||||
private:
|
||||
const PinPathNameLess pin_less_;
|
||||
Graph *graph_;
|
||||
Graph *&graph_;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
|
||||
#include "Map.hh"
|
||||
#include "NetworkClass.hh"
|
||||
|
|
@ -32,6 +33,7 @@ namespace sta {
|
|||
|
||||
using std::vector;
|
||||
using std::map;
|
||||
using std::array;
|
||||
|
||||
class DelayCalcObserver;
|
||||
class MultiDrvrNet;
|
||||
|
|
@ -118,6 +120,10 @@ public:
|
|||
const RiseFall *from_rf,
|
||||
const Edge *edge,
|
||||
const DcalcAnalysisPt *dcalc_ap);
|
||||
Slew edgeFromSlew(const Vertex *from_vertex,
|
||||
const RiseFall *from_rf,
|
||||
const TimingRole *role,
|
||||
const DcalcAnalysisPt *dcalc_ap);
|
||||
|
||||
protected:
|
||||
void seedInvalidDelays();
|
||||
|
|
@ -171,7 +177,8 @@ protected:
|
|||
bool findDriverEdgeDelays(Vertex *drvr_vertex,
|
||||
const MultiDrvrNet *multi_drvr,
|
||||
Edge *edge,
|
||||
ArcDelayCalc *arc_delay_calc);
|
||||
ArcDelayCalc *arc_delay_calc,
|
||||
array<bool, RiseFall::index_count> &delay_exists);
|
||||
bool findDriverArcDelays(Vertex *drvr_vertex,
|
||||
const MultiDrvrNet *multi_drvr,
|
||||
Edge *edge,
|
||||
|
|
@ -193,7 +200,8 @@ protected:
|
|||
const TimingArc *&arc);
|
||||
void initWireDelays(Vertex *drvr_vertex);
|
||||
void initRootSlews(Vertex *vertex);
|
||||
void zeroSlewAndWireDelays(Vertex *drvr_vertex);
|
||||
void zeroSlewAndWireDelays(Vertex *drvr_vertex,
|
||||
const RiseFall *rf);
|
||||
void findVertexDelay(Vertex *vertex,
|
||||
ArcDelayCalc *arc_delay_calc,
|
||||
bool propagate);
|
||||
|
|
|
|||
|
|
@ -73,7 +73,8 @@ typedef Map<const OperatingConditions*, LibertyCell*> ScaledCellMap;
|
|||
typedef Map<const OperatingConditions*, LibertyPort*> ScaledPortMap;
|
||||
typedef Map<const char *, ModeDef*, CharPtrLess> ModeDefMap;
|
||||
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 Vector<InternalPowerAttrs*> InternalPowerAttrsSeq;
|
||||
typedef Map<const char *, float, CharPtrLess> SupplyVoltageMap;
|
||||
|
|
@ -472,12 +473,12 @@ public:
|
|||
bool hasInferedRegTimingArcs() const { return has_infered_reg_timing_arcs_; }
|
||||
TestCell *testCell() const { return test_cell_; }
|
||||
bool isLatchData(LibertyPort *port);
|
||||
void latchEnable(TimingArcSet *arc_set,
|
||||
void latchEnable(const TimingArcSet *arc_set,
|
||||
// Return values.
|
||||
LibertyPort *&enable_port,
|
||||
FuncExpr *&enable_func,
|
||||
RiseFall *&enable_rf) const;
|
||||
RiseFall *latchCheckEnableEdge(TimingArcSet *check_set);
|
||||
const LibertyPort *&enable_port,
|
||||
const FuncExpr *&enable_func,
|
||||
const RiseFall *&enable_rf) const;
|
||||
const RiseFall *latchCheckEnableEdge(TimingArcSet *check_set);
|
||||
bool isDisabledConstraint() const { return is_disabled_constraint_; }
|
||||
LibertyCell *cornerCell(const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
|
|
@ -531,7 +532,7 @@ public:
|
|||
// Check all liberty cells to make sure they exist
|
||||
// for all the defined corners.
|
||||
static void checkLibertyCorners();
|
||||
void ensureVoltageWaveforms();
|
||||
void ensureVoltageWaveforms(const DcalcAnalysisPt *dcalc_ap);
|
||||
|
||||
protected:
|
||||
void addPort(ConcretePort *port);
|
||||
|
|
@ -539,18 +540,26 @@ protected:
|
|||
void setLibertyLibrary(LibertyLibrary *library);
|
||||
void makeLatchEnables(Report *report,
|
||||
Debug *debug);
|
||||
FuncExpr *findLatchEnableFunc(LibertyPort *data,
|
||||
LibertyPort *enable) const;
|
||||
FuncExpr *findLatchEnableFunc(const LibertyPort *d,
|
||||
const LibertyPort *en,
|
||||
const RiseFall *en_rf) const;
|
||||
LatchEnable *makeLatchEnable(LibertyPort *d,
|
||||
LibertyPort *en,
|
||||
const RiseFall *en_rf,
|
||||
LibertyPort *q,
|
||||
TimingArcSet *d_to_q,
|
||||
TimingArcSet *en_to_q,
|
||||
TimingArcSet *setup_check,
|
||||
Debug *debug);
|
||||
TimingArcSet *findLatchSetup(const LibertyPort *d,
|
||||
const LibertyPort *en,
|
||||
const RiseFall *en_rf,
|
||||
const LibertyPort *q,
|
||||
Report *report);
|
||||
void findDefaultCondArcs();
|
||||
void translatePresetClrCheckRoles();
|
||||
void inferLatchRoles(Debug *debug);
|
||||
void inferLatchRoles(Report *report,
|
||||
Debug *debug);
|
||||
void deleteInternalPowerAttrs();
|
||||
void makeTimingArcMap(Report *report);
|
||||
void makeTimingArcPortMaps();
|
||||
|
|
@ -596,6 +605,7 @@ protected:
|
|||
LatchEnableMap latch_d_to_q_map_;
|
||||
// Latch EN->D setup to LatchEnable.
|
||||
LatchEnableMap latch_check_map_;
|
||||
LatchEnableSeq latch_enables_;
|
||||
// Ports that have latch D->Q timing arc sets from them.
|
||||
LibertyPortSet latch_data_ports_;
|
||||
float ocv_arc_depth_;
|
||||
|
|
@ -792,20 +802,20 @@ public:
|
|||
DriverWaveform *driverWaveform(const RiseFall *rf) const;
|
||||
void setDriverWaveform(DriverWaveform *driver_waveform,
|
||||
const RiseFall *rf);
|
||||
void setClkTreeDelay(const TableModel *model,
|
||||
const RiseFall *from_rf,
|
||||
const RiseFall *to_rf,
|
||||
const MinMax *min_max);
|
||||
// Should be deprecated.
|
||||
float clkTreeDelay(float in_slew,
|
||||
const RiseFall *from_rf,
|
||||
const MinMax *min_max) const;
|
||||
float clkTreeDelay(float in_slew,
|
||||
const RiseFall *from_rf,
|
||||
const RiseFall *to_rf,
|
||||
const MinMax *min_max) const;
|
||||
// Assumes input slew of 0.0.
|
||||
RiseFallMinMax clkTreeDelays() const;
|
||||
float clkTreeDelay(float in_slew,
|
||||
const RiseFall *from_rf,
|
||||
const MinMax *min_max) const;
|
||||
void setClkTreeDelay(const TableModel *model,
|
||||
const RiseFall *from_rf,
|
||||
const RiseFall *to_rf,
|
||||
const MinMax *min_max);
|
||||
// deprecated 2024-06-22
|
||||
RiseFallMinMax clkTreeDelays() const __attribute__ ((deprecated));
|
||||
// deprecated 2024-02-27
|
||||
RiseFallMinMax clockTreePathDelays() const __attribute__ ((deprecated));
|
||||
|
||||
static bool equiv(const LibertyPort *port1,
|
||||
|
|
|
|||
|
|
@ -101,9 +101,7 @@ public:
|
|||
void
|
||||
deleteKeysContents()
|
||||
{
|
||||
for (auto key_value : this) {
|
||||
KEY key = key_value.first;
|
||||
VALUE value = key_value.second;
|
||||
for (const auto [key, value] : this) {
|
||||
delete key;
|
||||
delete value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,9 +108,9 @@ public:
|
|||
bool matches(const MinMax *min_max) const;
|
||||
bool matches(const MinMaxAll *min_max) const;
|
||||
static MinMaxAll *find(const char *min_max);
|
||||
// for (auto min_max : min_max->range()) {}
|
||||
// for (const auto min_max : min_max->range()) {}
|
||||
const std::vector<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_; }
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -513,6 +513,7 @@ public:
|
|||
LibertyPort *port,
|
||||
Net *net) = 0;
|
||||
// makePin/connectPin replaced by connect.
|
||||
// deprecated 2018-09-28
|
||||
virtual void connectPin(Pin *pin,
|
||||
Net *net) __attribute__ ((deprecated));
|
||||
// Disconnect pin from net.
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ public:
|
|||
// Find the parasitic node connected to pin.
|
||||
virtual ParasiticNode *findParasiticNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const = 0;
|
||||
// deprecated 2024-02-27
|
||||
virtual ParasiticNode *findNode(const Parasitic *parasitic,
|
||||
const Pin *pin) const __attribute__ ((deprecated));
|
||||
// Make a subnode of the parasitic network net connected to pin.
|
||||
|
|
@ -188,6 +189,7 @@ public:
|
|||
virtual const Pin *pin(const ParasiticNode *node) const = 0;
|
||||
virtual const Net *net(const ParasiticNode *node,
|
||||
const Network *network) const = 0;
|
||||
virtual unsigned netId(const ParasiticNode *node) const = 0;
|
||||
virtual bool isExternal(const ParasiticNode *node) const = 0;
|
||||
// Node capacitance to ground.
|
||||
virtual float nodeGndCap(const ParasiticNode *node) const = 0;
|
||||
|
|
@ -308,4 +310,17 @@ private:
|
|||
float coupling_cap_factor_;
|
||||
};
|
||||
|
||||
class ParasiticNodeLess
|
||||
{
|
||||
public:
|
||||
ParasiticNodeLess(const Parasitics *parasitics,
|
||||
const Network *network);
|
||||
ParasiticNodeLess(const ParasiticNodeLess &less);
|
||||
bool operator()(const ParasiticNode *node1,
|
||||
const ParasiticNode *node2) const;
|
||||
private:
|
||||
const Parasitics *parasitics_;
|
||||
const Network *network_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -136,7 +136,8 @@ public:
|
|||
virtual PathDelay *pathDelay() const;
|
||||
// This returns the crpr signed with respect to the check type.
|
||||
// Positive for setup, negative for hold.
|
||||
virtual Crpr commonClkPessimism(const StaState *sta) const;
|
||||
virtual Crpr checkCrpr(const StaState *sta) const;
|
||||
virtual Crpr crpr(const StaState *sta) const;
|
||||
virtual MultiCyclePath *multiCyclePath() const;
|
||||
virtual TimingArc *checkArc() const { return nullptr; }
|
||||
// PathEndDataCheck data clock path.
|
||||
|
|
@ -244,7 +245,7 @@ public:
|
|||
virtual float targetNonInterClkUncertainty(const StaState *sta) const;
|
||||
virtual float interClkUncertainty(const StaState *sta) const;
|
||||
virtual float targetClkUncertainty(const StaState *sta) const;
|
||||
virtual Crpr commonClkPessimism(const StaState *sta) const;
|
||||
virtual Crpr crpr(const StaState *sta) const;
|
||||
virtual Required requiredTime(const StaState *sta) const;
|
||||
virtual Slack slack(const StaState *sta) const;
|
||||
virtual Slack slackNoCrpr(const StaState *sta) const;
|
||||
|
|
@ -426,7 +427,7 @@ public:
|
|||
virtual Arrival targetClkArrivalNoCrpr(const StaState *sta) const;
|
||||
virtual Delay targetClkDelay(const StaState *sta) const;
|
||||
virtual Delay targetClkInsertionDelay(const StaState *sta) const;
|
||||
virtual Crpr commonClkPessimism(const StaState *sta) const;
|
||||
virtual Crpr crpr(const StaState *sta) const;
|
||||
virtual int exceptPathCmp(const PathEnd *path_end,
|
||||
const StaState *sta) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,8 @@ public:
|
|||
void deleteNetBefore(const Net *net);
|
||||
|
||||
// SWIG sdc interface.
|
||||
PortSeq allInputs(bool no_clks);
|
||||
PortSeq allOutputs();
|
||||
AnalysisType analysisType() { return analysis_type_; }
|
||||
void setAnalysisType(AnalysisType analysis_type);
|
||||
void setOperatingConditions(OperatingConditions *op_cond,
|
||||
|
|
@ -953,6 +955,13 @@ public:
|
|||
float &wire_cap,
|
||||
float &fanout,
|
||||
bool &has_net_load) const;
|
||||
void pinCaps(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max,
|
||||
float &pin_cap,
|
||||
float &wire_cap,
|
||||
float &fanout) const;
|
||||
void portExtFanout(const Port *port,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max,
|
||||
|
|
@ -1046,6 +1055,8 @@ public:
|
|||
bool bidirectDrvrSlewFromLoad(const Pin *pin) const;
|
||||
|
||||
protected:
|
||||
void portMembers(const Port *port,
|
||||
PortSeq &ports);
|
||||
void initVariables();
|
||||
void clearCycleAcctings();
|
||||
void removeLibertyAnnotations();
|
||||
|
|
@ -1231,13 +1242,6 @@ protected:
|
|||
void annotateHierClkLatency();
|
||||
void annotateHierClkLatency(const Pin *hpin,
|
||||
ClockLatency *latency);
|
||||
void pinCaps(const Pin *pin,
|
||||
const RiseFall *rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max,
|
||||
float &pin_cap,
|
||||
float &wire_cap,
|
||||
float &fanout) const;
|
||||
void netCaps(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const Corner *corner,
|
||||
|
|
@ -1252,7 +1256,8 @@ protected:
|
|||
const RiseFall *rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
float portCapacitance(Instance *inst, LibertyPort *port,
|
||||
float portCapacitance(Instance *inst,
|
||||
LibertyPort *port,
|
||||
const RiseFall *rf,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max) const;
|
||||
|
|
@ -1364,6 +1369,7 @@ protected:
|
|||
InstanceSet disabled_clk_gating_checks_inst_;
|
||||
PinSet disabled_clk_gating_checks_pin_;
|
||||
ExceptionPathSet exceptions_;
|
||||
size_t exception_id_; // Unique ID for exceptions.
|
||||
|
||||
bool have_thru_hpin_exceptions_;
|
||||
// First pin/clock/instance/net/edge exception point to exception set map.
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ typedef MinMaxAll SetupHoldAll;
|
|||
typedef Vector<ExceptionThru*> ExceptionThruSeq;
|
||||
typedef Set<LibertyPortPair, LibertyPortPairLess> LibertyPortPairSet;
|
||||
typedef Map<const Instance*, DisabledInstancePorts*> DisabledInstancePortsMap;
|
||||
typedef Map<const LibertyCell*, DisabledCellPorts*> DisabledCellPortsMap;
|
||||
typedef Map<LibertyCell*, DisabledCellPorts*> DisabledCellPortsMap;
|
||||
typedef MinMaxValues<float> ClockUncertainties;
|
||||
typedef Set<ExceptionPath*> ExceptionPathSet;
|
||||
typedef PinPair EdgePins;
|
||||
|
|
@ -107,12 +107,8 @@ class ExceptionState;
|
|||
class ExceptionStateLess
|
||||
{
|
||||
public:
|
||||
ExceptionStateLess(const Network *network);
|
||||
bool operator()(const ExceptionState *state1,
|
||||
const ExceptionState *state2) const;
|
||||
|
||||
private:
|
||||
const Network *network_;
|
||||
};
|
||||
|
||||
class ExceptionPath;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
#include "VertexVisitor.hh"
|
||||
#include "SearchClass.hh"
|
||||
#include "PowerClass.hh"
|
||||
#include "ArcDelayCalc.hh"
|
||||
#include "CircuitSim.hh"
|
||||
|
||||
struct Tcl_Interp;
|
||||
|
||||
|
|
@ -114,9 +116,8 @@ public:
|
|||
// linked network.
|
||||
void readNetlistBefore();
|
||||
// Return true if successful.
|
||||
bool linkDesign(const char *top_cell_name);
|
||||
bool linkMakeBlackBoxes() const;
|
||||
void setLinkMakeBlackBoxes(bool make);
|
||||
bool linkDesign(const char *top_cell_name,
|
||||
bool make_black_boxes);
|
||||
|
||||
// SDC Swig API.
|
||||
Instance *currentInstance() const;
|
||||
|
|
@ -773,7 +774,7 @@ public:
|
|||
const RiseFallBoth *rf,
|
||||
float slew);
|
||||
void writeSdf(const char *filename,
|
||||
Corner *corner,
|
||||
const Corner *corner,
|
||||
char divider,
|
||||
bool include_typ,
|
||||
int digits,
|
||||
|
|
@ -938,7 +939,7 @@ public:
|
|||
// bug that should be reported.
|
||||
void updateTiming(bool full);
|
||||
// Invalidate all delay calculations. Arrivals also invalidated.
|
||||
void delaysInvalid();
|
||||
void delaysInvalid() const;
|
||||
// Invalidate all arrival and required times.
|
||||
void arrivalsInvalid();
|
||||
PinSet startpointPins();
|
||||
|
|
@ -1236,7 +1237,7 @@ public:
|
|||
void setCmdCorner(Corner *corner);
|
||||
Corner *findCorner(const char *corner_name);
|
||||
bool multiCorner();
|
||||
void makeCorners(StringSet *corner_names);
|
||||
virtual void makeCorners(StringSet *corner_names);
|
||||
// Find all arc delays and vertex slews with delay calculator.
|
||||
virtual void findDelays();
|
||||
// Find arc delays and vertex slews thru to level of to_vertex.
|
||||
|
|
@ -1285,6 +1286,24 @@ public:
|
|||
const Corner *corner);
|
||||
PwrActivity findClkedActivity(const Pin *pin);
|
||||
|
||||
void writeGateSpice(ArcDcalcArgSeq gates,
|
||||
const char *spice_filename,
|
||||
const char *subckt_filename,
|
||||
const char *lib_subckt_filename,
|
||||
const char *model_filename,
|
||||
const char *power_name,
|
||||
const char *gnd_name,
|
||||
CircuitSim ckt_sim,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
void writeGateGnuplot(ArcDcalcArgSeq gates,
|
||||
PinSet plot_pins,
|
||||
const char *spice_waveform_filename,
|
||||
const char *csv_filename,
|
||||
const char *gnuplot_filename,
|
||||
const Corner *corner,
|
||||
const MinMax *min_max);
|
||||
|
||||
void writeTimingModel(const char *lib_name,
|
||||
const char *cell_name,
|
||||
const char *filename,
|
||||
|
|
@ -1432,7 +1451,6 @@ protected:
|
|||
ReportPath *report_path_;
|
||||
Power *power_;
|
||||
Tcl_Interp *tcl_interp_;
|
||||
bool link_make_black_boxes_;
|
||||
bool update_genclks_;
|
||||
EquivCells *equiv_cells_;
|
||||
bool graph_sdc_annotated_;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class Table1;
|
|||
typedef Vector<float> FloatSeq;
|
||||
typedef Vector<FloatSeq*> FloatTable;
|
||||
typedef Vector<Table1*> Table1Seq;
|
||||
typedef Table1 Waveform;
|
||||
|
||||
TableAxisVariable
|
||||
stringTableAxisVariable(const char *variable);
|
||||
|
|
@ -66,6 +67,7 @@ public:
|
|||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const override;
|
||||
// deprecated 2024-01-07
|
||||
// related_out_cap arg removed.
|
||||
void gateDelay(const Pvt *pvt,
|
||||
float in_slew,
|
||||
|
|
@ -455,9 +457,10 @@ public:
|
|||
// Return values.
|
||||
size_t &index,
|
||||
bool &exists) const;
|
||||
size_t findAxisClosestIndex(float value) const;
|
||||
FloatSeq *values() const { return values_; }
|
||||
float min() const { return (*values_)[0]; }
|
||||
float max() const { return (*values_)[values_->size() - 1]; }
|
||||
float min() const;
|
||||
float max() const;
|
||||
|
||||
private:
|
||||
TableAxisVariable variable_;
|
||||
|
|
@ -492,26 +495,43 @@ public:
|
|||
const RiseFall *rf() const { return rf_; }
|
||||
const TableAxis *slewAxis() const { return slew_axis_.get(); }
|
||||
const TableAxis *capAxis() const { return cap_axis_.get(); }
|
||||
Table1 voltageWaveform(float in_slew,
|
||||
float load_cap);
|
||||
float voltageTime(float in_slew,
|
||||
float load_cap,
|
||||
float voltage);
|
||||
const Table1 *currentWaveform(float slew,
|
||||
float cap);
|
||||
// Make voltage wavefroms from liberty time/current values.
|
||||
// Required before voltageTime, timeVoltage, voltageCurrent.
|
||||
void makeVoltageWaveforms(float vdd);
|
||||
float timeCurrent(float slew,
|
||||
float cap,
|
||||
float time);
|
||||
float timeVoltage(float slew,
|
||||
float cap,
|
||||
float time);
|
||||
float voltageTime(float in_slew,
|
||||
float load_cap,
|
||||
float voltage);
|
||||
float voltageCurrent(float slew,
|
||||
float cap,
|
||||
float volt);
|
||||
float referenceTime(float slew);
|
||||
void makeVoltageWaveforms(float vdd);
|
||||
float beginTime(float slew,
|
||||
float cap);
|
||||
float endTime(float slew,
|
||||
float cap);
|
||||
static bool checkAxes(const TableTemplate *tbl_template);
|
||||
|
||||
Table1 currentWaveform(float slew,
|
||||
float cap);
|
||||
// Waveform closest to slew/cap; no interpolation.
|
||||
const Table1 *currentWaveformRaw(float slew,
|
||||
float cap);
|
||||
Table1 voltageWaveform(float in_slew,
|
||||
float load_cap);
|
||||
// Waveform closest to slew/cap; no interpolation.
|
||||
const Table1 *voltageWaveformRaw(float slew,
|
||||
float cap);
|
||||
Table1 voltageCurrentWaveform(float slew,
|
||||
float cap);
|
||||
// V/I for last segment of min slew/max cap.
|
||||
float finalResistance();
|
||||
|
||||
private:
|
||||
void findVoltages(size_t wave_index,
|
||||
float cap);
|
||||
|
|
@ -519,27 +539,30 @@ private:
|
|||
float cap,
|
||||
float axis_value,
|
||||
Table1Seq &waveforms);
|
||||
float voltageTime1(float voltage,
|
||||
float beginEndTime(float slew,
|
||||
float cap,
|
||||
bool begin);
|
||||
double voltageTime1(double volt,
|
||||
double dx1,
|
||||
double dx2,
|
||||
size_t wave_index00,
|
||||
size_t wave_index01,
|
||||
size_t wave_index10,
|
||||
size_t wave_index11);
|
||||
float voltageTime2(float volt,
|
||||
size_t wave_index);
|
||||
void waveformMinMaxTime(float slew,
|
||||
float cap,
|
||||
Table1Seq &waveforms,
|
||||
// Return values.
|
||||
float &min_time,
|
||||
float &max_time);
|
||||
|
||||
// Row.
|
||||
TableAxisPtr slew_axis_;
|
||||
// Column.
|
||||
TableAxisPtr cap_axis_;
|
||||
const RiseFall *rf_;
|
||||
Table1Seq current_waveforms_;
|
||||
Table1Seq current_waveforms_; // from liberty
|
||||
Table1Seq voltage_waveforms_;
|
||||
Table1Seq voltage_currents_;
|
||||
FloatTable voltage_times_;
|
||||
Table1 *ref_times_;
|
||||
float vdd_;
|
||||
static constexpr size_t voltage_waveform_step_count_ = 20;
|
||||
static constexpr size_t voltage_waveform_step_count_ = 100;
|
||||
};
|
||||
|
||||
class DriverWaveform
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ namespace sta {
|
|||
|
||||
class TimingArcAttrs;
|
||||
class WireTimingArc;
|
||||
class GateTableModel;
|
||||
class DcalcAnalysisPt;
|
||||
|
||||
typedef int TimingArcIndex;
|
||||
typedef Vector<TimingArc*> TimingArcSeq;
|
||||
|
|
@ -147,7 +149,7 @@ public:
|
|||
TimingRole *role() const { return role_; };
|
||||
TimingSense sense() const;
|
||||
// Rise/fall if the arc set is rising_edge or falling_edge.
|
||||
RiseFall *isRisingFallingEdge() const;
|
||||
const RiseFall *isRisingFallingEdge() const;
|
||||
size_t arcCount() const { return arcs_.size(); }
|
||||
TimingArcSeq &arcs() { return arcs_; }
|
||||
// Return 1 or 2 arcs matching from transition.
|
||||
|
|
@ -235,8 +237,11 @@ public:
|
|||
TimingSense sense() const;
|
||||
// Index in TimingArcSet.
|
||||
unsigned index() const { return index_; }
|
||||
TimingModel *model(const OperatingConditions *op_cond) const;
|
||||
TimingModel *model() const { return model_; }
|
||||
GateTimingModel *gateModel(const DcalcAnalysisPt *dcalc_ap) const;
|
||||
CheckTimingModel *checkModel(const DcalcAnalysisPt *dcalc_ap) const;
|
||||
GateTableModel *gateTableModel() const;
|
||||
GateTableModel *gateTableModel(const DcalcAnalysisPt *dcalc_ap) const;
|
||||
const TimingArc *cornerArc(int ap_index) const;
|
||||
void setCornerArc(TimingArc *corner_arc,
|
||||
int ap_index);
|
||||
|
|
@ -247,6 +252,7 @@ public:
|
|||
const TimingArc *arc2);
|
||||
|
||||
protected:
|
||||
TimingModel *model(const DcalcAnalysisPt *dcalc_ap) const;
|
||||
void setIndex(unsigned index);
|
||||
void addScaledModel(const OperatingConditions *op_cond,
|
||||
TimingModel *scaled_model);
|
||||
|
|
|
|||
|
|
@ -97,9 +97,9 @@ public:
|
|||
RiseFall *asRiseFall() const { return as_rise_fall_; }
|
||||
// Find transition corresponding to string.
|
||||
static RiseFallBoth *find(const char *tr_str);
|
||||
// for (auto tr : min_max->range()) {}
|
||||
// for (const auto rf : rf->range()) {}
|
||||
const std::vector<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_; }
|
||||
|
||||
static const int index_count = 3;
|
||||
|
|
|
|||
|
|
@ -121,10 +121,8 @@ LibertyLibrary::~LibertyLibrary()
|
|||
delete units_;
|
||||
ocv_derate_map_.deleteContents();
|
||||
|
||||
for (auto name_volt : supply_voltage_map_) {
|
||||
const char *supply_name = name_volt.first;
|
||||
for (auto [supply_name, volt] : supply_voltage_map_)
|
||||
stringDelete(supply_name);
|
||||
}
|
||||
delete buffers_;
|
||||
delete inverters_;
|
||||
driver_waveform_map_.deleteContents();
|
||||
|
|
@ -204,8 +202,8 @@ BusDclSeq
|
|||
LibertyLibrary::busDcls() const
|
||||
{
|
||||
BusDclSeq dcls;
|
||||
for (auto name_dcl : bus_dcls_)
|
||||
dcls.push_back(name_dcl.second);
|
||||
for (auto [name, dcl] : bus_dcls_)
|
||||
dcls.push_back(dcl);
|
||||
return dcls;
|
||||
}
|
||||
|
||||
|
|
@ -228,10 +226,8 @@ LibertyLibrary::tableTemplates() const
|
|||
{
|
||||
TableTemplateSeq tbl_templates;
|
||||
for (int type = 0; type < table_template_type_count; type++) {
|
||||
for (auto name_template : template_maps_[type]) {
|
||||
TableTemplate *tbl_template = name_template.second;
|
||||
for (auto [name, tbl_template] : template_maps_[type])
|
||||
tbl_templates.push_back(tbl_template);
|
||||
}
|
||||
}
|
||||
return tbl_templates;
|
||||
}
|
||||
|
|
@ -950,7 +946,7 @@ LibertyCell::LibertyCell(LibertyLibrary *library,
|
|||
LibertyCell::~LibertyCell()
|
||||
{
|
||||
mode_defs_.deleteContents();
|
||||
latch_d_to_q_map_.deleteContents();
|
||||
latch_enables_.deleteContents();
|
||||
|
||||
timing_arc_sets_.deleteContents();
|
||||
port_timing_arc_set_map_.deleteContents();
|
||||
|
|
@ -1306,14 +1302,13 @@ LibertyCell::finish(bool infer_latches,
|
|||
findDefaultCondArcs();
|
||||
makeLatchEnables(report, debug);
|
||||
if (infer_latches)
|
||||
inferLatchRoles(debug);
|
||||
inferLatchRoles(report, debug);
|
||||
}
|
||||
|
||||
void
|
||||
LibertyCell::findDefaultCondArcs()
|
||||
{
|
||||
for (auto port_pair_set : port_timing_arc_set_map_) {
|
||||
TimingArcSetSeq *sets = port_pair_set.second;
|
||||
for (auto [port_pair, sets] : port_timing_arc_set_map_) {
|
||||
bool has_cond_arcs = false;
|
||||
for (auto set : *sets) {
|
||||
if (set->cond()) {
|
||||
|
|
@ -1675,7 +1670,7 @@ class LatchEnable
|
|||
public:
|
||||
LatchEnable(LibertyPort *data,
|
||||
LibertyPort *enable,
|
||||
RiseFall *enable_edge,
|
||||
const RiseFall *enable_edge,
|
||||
FuncExpr *enable_func,
|
||||
LibertyPort *output,
|
||||
TimingArcSet *d_to_q,
|
||||
|
|
@ -1685,7 +1680,7 @@ public:
|
|||
LibertyPort *output() const { return output_; }
|
||||
LibertyPort *enable() const { return enable_; }
|
||||
FuncExpr *enableFunc() const { return enable_func_; }
|
||||
RiseFall *enableEdge() const { return enable_edge_; }
|
||||
const RiseFall *enableEdge() const { return enable_edge_; }
|
||||
TimingArcSet *dToQ() const { return d_to_q_; }
|
||||
TimingArcSet *enToQ() const { return en_to_q_; }
|
||||
TimingArcSet *setupCheck() const { return setup_check_; }
|
||||
|
|
@ -1693,7 +1688,7 @@ public:
|
|||
private:
|
||||
LibertyPort *data_;
|
||||
LibertyPort *enable_;
|
||||
RiseFall *enable_edge_;
|
||||
const RiseFall *enable_edge_;
|
||||
FuncExpr *enable_func_;
|
||||
LibertyPort *output_;
|
||||
TimingArcSet *d_to_q_;
|
||||
|
|
@ -1703,7 +1698,7 @@ private:
|
|||
|
||||
LatchEnable::LatchEnable(LibertyPort *data,
|
||||
LibertyPort *enable,
|
||||
RiseFall *enable_edge,
|
||||
const RiseFall *enable_edge,
|
||||
FuncExpr *enable_func,
|
||||
LibertyPort *output,
|
||||
TimingArcSet *d_to_q,
|
||||
|
|
@ -1736,66 +1731,101 @@ LibertyCell::makeLatchEnables(Report *report,
|
|||
for (TimingArcSet *d_to_q : timingArcSets(nullptr, q)) {
|
||||
if (d_to_q->role() == TimingRole::latchDtoQ()) {
|
||||
LibertyPort *d = d_to_q->from();
|
||||
for (TimingArcSet *setup_check : timingArcSets(en, d)) {
|
||||
if (setup_check->role() == TimingRole::setup()) {
|
||||
LatchEnable *latch_enable = makeLatchEnable(d, en, q, d_to_q,
|
||||
en_to_q,
|
||||
setup_check,
|
||||
debug);
|
||||
for (TimingArc *check_arc : setup_check->arcs()) {
|
||||
RiseFall *en_rf = latch_enable->enableEdge();
|
||||
RiseFall *check_rf = check_arc->fromEdge()->asRiseFall();
|
||||
if (check_rf == en_rf)
|
||||
report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.",
|
||||
library_->name(),
|
||||
name_,
|
||||
en->name(),
|
||||
q->name(),
|
||||
en_rf == RiseFall::rise()?"rising":"falling",
|
||||
en->name(),
|
||||
d->name(),
|
||||
check_rf==RiseFall::rise()?"rising":"falling");
|
||||
FuncExpr *en_func = latch_enable->enableFunc();
|
||||
if (en_func) {
|
||||
TimingSense en_sense = en_func->portTimingSense(en);
|
||||
if (en_sense == TimingSense::positive_unate
|
||||
&& en_rf != RiseFall::rise())
|
||||
report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.",
|
||||
library_->name(),
|
||||
name_,
|
||||
en->name(),
|
||||
q->name(),
|
||||
en_rf == RiseFall::rise()?"rising":"falling");
|
||||
else if (en_sense == TimingSense::negative_unate
|
||||
&& en_rf != RiseFall::fall())
|
||||
report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.",
|
||||
library_->name(),
|
||||
name_,
|
||||
en->name(),
|
||||
q->name(),
|
||||
en_rf == RiseFall::rise()?"rising":"falling");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
|
||||
if (en_rf) {
|
||||
TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report);
|
||||
LatchEnable *latch_enable = makeLatchEnable(d, en, en_rf, q, d_to_q,
|
||||
en_to_q,
|
||||
setup_check,
|
||||
debug);
|
||||
FuncExpr *en_func = latch_enable->enableFunc();
|
||||
if (en_func) {
|
||||
TimingSense en_sense = en_func->portTimingSense(en);
|
||||
if (en_sense == TimingSense::positive_unate
|
||||
&& en_rf != RiseFall::rise())
|
||||
report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.",
|
||||
library_->name(),
|
||||
name_,
|
||||
en->name(),
|
||||
q->name(),
|
||||
en_rf == RiseFall::rise()?"rising":"falling");
|
||||
else if (en_sense == TimingSense::negative_unate
|
||||
&& en_rf != RiseFall::fall())
|
||||
report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.",
|
||||
library_->name(),
|
||||
name_,
|
||||
en->name(),
|
||||
q->name(),
|
||||
en_rf == RiseFall::rise()?"rising":"falling");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimingArcSet *
|
||||
LibertyCell::findLatchSetup(const LibertyPort *d,
|
||||
const LibertyPort *en,
|
||||
const RiseFall *en_rf,
|
||||
const LibertyPort *q,
|
||||
Report *report)
|
||||
{
|
||||
TimingArcSetSeq en_d_arcs = timingArcSets(en, d);
|
||||
// First search for setup checks with the correct clock edge.
|
||||
for (TimingArcSet *arc_set : en_d_arcs) {
|
||||
if (arc_set->role() == TimingRole::setup()) {
|
||||
for (TimingArc *arc : arc_set->arcs()) {
|
||||
const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
|
||||
if (from_rf == en_rf->opposite())
|
||||
return arc_set;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then search for setup checks with the opposite clock edge.
|
||||
for (TimingArcSet *arc_set : en_d_arcs) {
|
||||
if (arc_set->role() == TimingRole::setup()) {
|
||||
for (TimingArc *arc : arc_set->arcs()) {
|
||||
const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
|
||||
if (from_rf == en_rf) {
|
||||
report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.",
|
||||
library_->name(),
|
||||
name_,
|
||||
en->name(),
|
||||
q->name(),
|
||||
en_rf == RiseFall::rise() ? "rising" : "falling",
|
||||
en->name(),
|
||||
d->name(),
|
||||
from_rf == RiseFall::rise() ? "rising" : "falling");
|
||||
return arc_set;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FuncExpr *
|
||||
LibertyCell::findLatchEnableFunc(LibertyPort *data,
|
||||
LibertyPort *enable) const
|
||||
LibertyCell::findLatchEnableFunc(const LibertyPort *d,
|
||||
const LibertyPort *en,
|
||||
const RiseFall *en_rf) const
|
||||
{
|
||||
for (auto seq : sequentials_) {
|
||||
if (seq->isLatch()
|
||||
&& seq->data()
|
||||
&& seq->data()->hasPort(data)
|
||||
&& seq->data()->hasPort(d)
|
||||
&& seq->clock()
|
||||
&& seq->clock()->hasPort(enable))
|
||||
return seq->clock();
|
||||
&& seq->clock()->hasPort(en)) {
|
||||
FuncExpr *en_func = seq->clock();
|
||||
TimingSense en_sense = en_func->portTimingSense(en);
|
||||
if ((en_sense == TimingSense::positive_unate
|
||||
&& en_rf == RiseFall::rise())
|
||||
|| (en_sense == TimingSense::negative_unate
|
||||
&& en_rf == RiseFall::fall()))
|
||||
return seq->clock();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -1803,29 +1833,36 @@ LibertyCell::findLatchEnableFunc(LibertyPort *data,
|
|||
LatchEnable *
|
||||
LibertyCell::makeLatchEnable(LibertyPort *d,
|
||||
LibertyPort *en,
|
||||
const RiseFall *en_rf,
|
||||
LibertyPort *q,
|
||||
TimingArcSet *d_to_q,
|
||||
TimingArcSet *en_to_q,
|
||||
TimingArcSet *setup_check,
|
||||
Debug *debug)
|
||||
{
|
||||
RiseFall *en_rf = en_to_q->isRisingFallingEdge();
|
||||
FuncExpr *en_func = findLatchEnableFunc(d, en);
|
||||
FuncExpr *en_func = findLatchEnableFunc(d, en, en_rf);
|
||||
LatchEnable *latch_enable = new LatchEnable(d, en, en_rf, en_func, q,
|
||||
d_to_q, en_to_q, setup_check);
|
||||
// Multiple enables for D->Q pairs are not supported.
|
||||
if (latch_d_to_q_map_[d_to_q])
|
||||
delete latch_d_to_q_map_[d_to_q];
|
||||
latch_enables_.push_back(latch_enable);
|
||||
latch_d_to_q_map_[d_to_q] = latch_enable;
|
||||
latch_check_map_[setup_check] = latch_enable;
|
||||
latch_data_ports_.insert(d);
|
||||
debugPrint(debug, "liberty", 2, "latch d=%s en=%s q=%s",
|
||||
d->name(), en->name(), q->name());
|
||||
debugPrint(debug, "liberty_latch", 1,
|
||||
"latch %s -> %s | %s %s -> %s | %s %s -> %s setup",
|
||||
d->name(),
|
||||
q->name(),
|
||||
en->name(),
|
||||
en_rf->shortName(),
|
||||
q->name(),
|
||||
en->name(),
|
||||
setup_check->arcs()[0]->fromEdge()->asRiseFall()->shortName(),
|
||||
q->name());
|
||||
return latch_enable;
|
||||
}
|
||||
|
||||
void
|
||||
LibertyCell::inferLatchRoles(Debug *debug)
|
||||
LibertyCell::inferLatchRoles(Report *report,
|
||||
Debug *debug)
|
||||
{
|
||||
if (hasInferedRegTimingArcs()) {
|
||||
// Hunt down potential latch D/EN/Q triples.
|
||||
|
|
@ -1845,15 +1882,14 @@ LibertyCell::inferLatchRoles(Debug *debug)
|
|||
// Previously identified as D->Q arc.
|
||||
|| d_to_q_role == TimingRole::latchDtoQ()) {
|
||||
LibertyPort *d = d_to_q->from();
|
||||
// Check for setup check from en -> d.
|
||||
for (TimingArcSet *setup_check : timingArcSets(en, d)) {
|
||||
if (setup_check->role() == TimingRole::setup()) {
|
||||
makeLatchEnable(d, en, q, d_to_q, en_to_q, setup_check, debug);
|
||||
d_to_q->setRole(TimingRole::latchDtoQ());
|
||||
en_to_q->setRole(TimingRole::latchEnToQ());
|
||||
}
|
||||
}
|
||||
}
|
||||
const RiseFall *en_rf = en_to_q->isRisingFallingEdge();
|
||||
if (en_rf) {
|
||||
TimingArcSet *setup_check = findLatchSetup(d, en, en_rf, q, report);
|
||||
makeLatchEnable(d, en, en_rf, q, d_to_q, en_to_q, setup_check, debug);
|
||||
d_to_q->setRole(TimingRole::latchDtoQ());
|
||||
en_to_q->setRole(TimingRole::latchEnToQ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1867,22 +1903,26 @@ LibertyCell::isLatchData(LibertyPort *port)
|
|||
}
|
||||
|
||||
void
|
||||
LibertyCell::latchEnable(TimingArcSet *d_to_q_set,
|
||||
LibertyCell::latchEnable(const TimingArcSet *d_to_q_set,
|
||||
// Return values.
|
||||
LibertyPort *&enable_port,
|
||||
FuncExpr *&enable_func,
|
||||
RiseFall *&enable_edge) const
|
||||
const LibertyPort *&enable_port,
|
||||
const FuncExpr *&enable_func,
|
||||
const RiseFall *&enable_edge) const
|
||||
{
|
||||
enable_port = nullptr;
|
||||
LatchEnable *latch_enable = latch_d_to_q_map_.findKey(d_to_q_set);
|
||||
if (latch_enable) {
|
||||
enable_port = latch_enable->enable();
|
||||
enable_func = latch_enable->enableFunc();
|
||||
enable_edge = latch_enable->enableEdge();
|
||||
}
|
||||
else {
|
||||
enable_port = nullptr;
|
||||
enable_func = nullptr;
|
||||
enable_edge = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RiseFall *
|
||||
const RiseFall *
|
||||
LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set)
|
||||
{
|
||||
LatchEnable *latch_enable = latch_check_map_.findKey(check_set);
|
||||
|
|
@ -1893,7 +1933,7 @@ LibertyCell::latchCheckEnableEdge(TimingArcSet *check_set)
|
|||
}
|
||||
|
||||
void
|
||||
LibertyCell::ensureVoltageWaveforms()
|
||||
LibertyCell::ensureVoltageWaveforms(const DcalcAnalysisPt *dcalc_ap)
|
||||
{
|
||||
if (!have_voltage_waveforms_) {
|
||||
float vdd = 0.0; // shutup gcc
|
||||
|
|
@ -1903,7 +1943,7 @@ LibertyCell::ensureVoltageWaveforms()
|
|||
criticalError(1120, "library missing vdd");
|
||||
for (TimingArcSet *arc_set : timingArcSets()) {
|
||||
for (TimingArc *arc : arc_set->arcs()) {
|
||||
GateTableModel*model = dynamic_cast<GateTableModel*>(arc->model());
|
||||
GateTableModel *model = arc->gateTableModel(dcalc_ap);
|
||||
if (model) {
|
||||
OutputWaveforms *output_waveforms = model->outputWaveforms();
|
||||
if (output_waveforms)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -695,22 +695,22 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group)
|
|||
}
|
||||
|
||||
bool missing_threshold = false;
|
||||
for (auto tr : RiseFall::range()) {
|
||||
int tr_index = tr->index();
|
||||
if (!have_input_threshold_[tr_index]) {
|
||||
libWarn(1145, group, "input_threshold_pct_%s not found.", tr->name());
|
||||
for (auto rf : RiseFall::range()) {
|
||||
int rf_index = rf->index();
|
||||
if (!have_input_threshold_[rf_index]) {
|
||||
libWarn(1145, group, "input_threshold_pct_%s not found.", rf->name());
|
||||
missing_threshold = true;
|
||||
}
|
||||
if (!have_output_threshold_[tr_index]) {
|
||||
libWarn(1146, group, "output_threshold_pct_%s not found.", tr->name());
|
||||
if (!have_output_threshold_[rf_index]) {
|
||||
libWarn(1146, group, "output_threshold_pct_%s not found.", rf->name());
|
||||
missing_threshold = true;
|
||||
}
|
||||
if (!have_slew_lower_threshold_[tr_index]) {
|
||||
libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", tr->name());
|
||||
if (!have_slew_lower_threshold_[rf_index]) {
|
||||
libWarn(1147, group, "slew_lower_threshold_pct_%s not found.", rf->name());
|
||||
missing_threshold = true;
|
||||
}
|
||||
if (!have_slew_upper_threshold_[tr_index]) {
|
||||
libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", tr->name());
|
||||
if (!have_slew_upper_threshold_[rf_index]) {
|
||||
libWarn(1148, group, "slew_upper_threshold_pct_%s not found.", rf->name());
|
||||
missing_threshold = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -3927,8 +3927,8 @@ LibertyReader::endTiming(LibertyGroup *group)
|
|||
{
|
||||
if (timing_) {
|
||||
// Set scale factor type in constraint tables.
|
||||
for (auto tr : RiseFall::range()) {
|
||||
TableModel *model = timing_->constraint(tr);
|
||||
for (auto rf : RiseFall::range()) {
|
||||
TableModel *model = timing_->constraint(rf);
|
||||
if (model) {
|
||||
ScaleFactorType type=timingTypeScaleFactorType(timing_->attrs()->timingType());
|
||||
model->setScaleFactorType(type);
|
||||
|
|
@ -5110,13 +5110,13 @@ LibertyReader::endOcvDerateFactors(LibertyGroup *)
|
|||
{
|
||||
if (ocv_derate_) {
|
||||
for (auto early_late : derate_type_->range()) {
|
||||
for (auto tr : rf_type_->range()) {
|
||||
for (auto rf : rf_type_->range()) {
|
||||
if (path_type_ == PathType::clk_and_data) {
|
||||
ocv_derate_->setDerateTable(tr, early_late, PathType::clk, table_);
|
||||
ocv_derate_->setDerateTable(tr, early_late, PathType::data, table_);
|
||||
ocv_derate_->setDerateTable(rf, early_late, PathType::clk, table_);
|
||||
ocv_derate_->setDerateTable(rf, early_late, PathType::data, table_);
|
||||
}
|
||||
else
|
||||
ocv_derate_->setDerateTable(tr, early_late, path_type_, table_);
|
||||
ocv_derate_->setDerateTable(rf, early_late, path_type_, table_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,11 +217,11 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
|
|||
if (axis3)
|
||||
fprintf(stream_, " variable_3 : %s;\n",
|
||||
tableVariableString(axis3->variable()));
|
||||
if (axis1)
|
||||
if (axis1 && axis1->values())
|
||||
writeTableAxis4(axis1, 1);
|
||||
if (axis2)
|
||||
if (axis2 && axis2->values())
|
||||
writeTableAxis4(axis2, 2);
|
||||
if (axis3)
|
||||
if (axis3 && axis3->values())
|
||||
writeTableAxis4(axis3, 3);
|
||||
fprintf(stream_, " }\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ using std::max;
|
|||
using std::abs;
|
||||
using std::make_shared;
|
||||
|
||||
size_t
|
||||
findValueIndex(float value,
|
||||
const FloatSeq *values);
|
||||
static void
|
||||
deleteSigmaModels(TableModel *models[EarlyLate::index_count]);
|
||||
static string
|
||||
|
|
@ -1425,6 +1428,25 @@ TableAxis::~TableAxis()
|
|||
delete values_;
|
||||
}
|
||||
|
||||
float
|
||||
TableAxis::min() const
|
||||
{
|
||||
if (!values_->empty())
|
||||
return (*values_)[0];
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float
|
||||
TableAxis::max() const
|
||||
{
|
||||
size_t size = values_->size();
|
||||
if (size > 0)
|
||||
return (*values_)[values_->size() - 1];
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
bool
|
||||
TableAxis::inBounds(float value) const
|
||||
{
|
||||
|
|
@ -1434,14 +1456,22 @@ TableAxis::inBounds(float value) const
|
|||
&& value <= (*values_)[size - 1];
|
||||
}
|
||||
|
||||
// Bisection search.
|
||||
size_t
|
||||
TableAxis::findAxisIndex(float value) const
|
||||
{
|
||||
size_t size = values_->size();
|
||||
if (size <= 1 || value <= (*values_)[0])
|
||||
return findValueIndex(value, values_);
|
||||
}
|
||||
|
||||
// Bisection search.
|
||||
// Assumes values are monotonically increasing.
|
||||
size_t
|
||||
findValueIndex(float value,
|
||||
const FloatSeq *values)
|
||||
{
|
||||
size_t size = values->size();
|
||||
if (size <= 1 || value <= (*values)[0])
|
||||
return 0;
|
||||
else if (value >= (*values_)[size - 1])
|
||||
else if (value >= (*values)[size - 1])
|
||||
// Return max_index-1 for value too large so interpolation pts are index,index+1.
|
||||
return size - 2;
|
||||
else {
|
||||
|
|
@ -1449,7 +1479,7 @@ TableAxis::findAxisIndex(float value) const
|
|||
int upper = size;
|
||||
while (upper - lower > 1) {
|
||||
int mid = (upper + lower) >> 1;
|
||||
if (value >= (*values_)[mid])
|
||||
if (value >= (*values)[mid])
|
||||
lower = mid;
|
||||
else
|
||||
upper = mid;
|
||||
|
|
@ -1486,6 +1516,31 @@ TableAxis::findAxisIndex(float value,
|
|||
exists = false;
|
||||
}
|
||||
|
||||
size_t
|
||||
TableAxis::findAxisClosestIndex(float value) const
|
||||
{
|
||||
size_t size = values_->size();
|
||||
if (size <= 1 || value <= (*values_)[0])
|
||||
return 0;
|
||||
else if (value >= (*values_)[size - 1])
|
||||
return size - 1;
|
||||
else {
|
||||
int lower = -1;
|
||||
int upper = size;
|
||||
while (upper - lower > 1) {
|
||||
int mid = (upper + lower) >> 1;
|
||||
if (value >= (*values_)[mid])
|
||||
lower = mid;
|
||||
else
|
||||
upper = mid;
|
||||
}
|
||||
if ((value - (*values_)[lower]) < ((*values_)[upper] - value))
|
||||
return lower;
|
||||
else
|
||||
return upper;
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
TableAxis::variableString() const
|
||||
{
|
||||
|
|
@ -1588,7 +1643,6 @@ OutputWaveforms::~OutputWaveforms()
|
|||
current_waveforms_.deleteContents();
|
||||
voltage_waveforms_.deleteContents();
|
||||
voltage_currents_.deleteContents();
|
||||
voltage_times_.deleteContents();
|
||||
delete ref_times_;
|
||||
}
|
||||
|
||||
|
|
@ -1616,10 +1670,9 @@ OutputWaveforms::makeVoltageWaveforms(float vdd)
|
|||
size_t size = current_waveforms_.size();
|
||||
voltage_waveforms_.resize(size);
|
||||
voltage_currents_.resize(size);
|
||||
voltage_times_.resize(size);
|
||||
size_t cap_count = cap_axis_->size();
|
||||
for (size_t slew_index = 0; slew_index < slew_axis_->size(); slew_index++) {
|
||||
for (size_t cap_index = 0; cap_index < cap_axis_->size(); cap_index++) {
|
||||
for (size_t cap_index = 0; cap_index < cap_count; cap_index++) {
|
||||
size_t wave_index = slew_index * cap_count + cap_index;
|
||||
findVoltages(wave_index, cap_axis_->axisValue(cap_index));
|
||||
}
|
||||
|
|
@ -1650,6 +1703,7 @@ OutputWaveforms::findVoltages(size_t wave_index,
|
|||
prev_time = time;
|
||||
prev_current = current;
|
||||
}
|
||||
(*volts)[volts->size() - 1] = vdd_;
|
||||
Table1 *volt_table = new Table1(volts, currents->axis1ptr());
|
||||
voltage_waveforms_[wave_index] = volt_table;
|
||||
|
||||
|
|
@ -1660,39 +1714,33 @@ OutputWaveforms::findVoltages(size_t wave_index,
|
|||
FloatSeq *currents1 = new FloatSeq(*currents->values());
|
||||
Table1 *volt_currents = new Table1(currents1, volt_axis);
|
||||
voltage_currents_[wave_index] = volt_currents;
|
||||
|
||||
// Sample the voltage waveform at uniform intervals to speed up
|
||||
// voltage time lookup.
|
||||
FloatSeq *voltage_times = new FloatSeq;
|
||||
float volt_step = vdd_ / voltage_waveform_step_count_;
|
||||
size_t i = 0;
|
||||
float time0 = time_axis->axisValue(i);
|
||||
float volt0 = (*volts)[i];
|
||||
i = 1;
|
||||
float time1 = time_axis->axisValue(i);
|
||||
float volt1 = (*volts)[i];
|
||||
for (size_t v = 0; v <= voltage_waveform_step_count_; v++) {
|
||||
float volt3 = v * volt_step;
|
||||
while (volt3 > volt1 && i < volts->size() - 1) {
|
||||
time0 = time1;
|
||||
volt0 = volt1;
|
||||
i++;
|
||||
time1 = time_axis->axisValue(i);
|
||||
volt1 = (*volts)[i];
|
||||
}
|
||||
float time3 = time0 + (time1 - time0) * (volt3 - volt0) / (volt1 - volt0);
|
||||
voltage_times->push_back(time3);
|
||||
}
|
||||
voltage_times_[wave_index] = voltage_times;
|
||||
}
|
||||
|
||||
const Table1 *
|
||||
Table1
|
||||
OutputWaveforms::currentWaveform(float slew,
|
||||
float cap)
|
||||
{
|
||||
size_t slew_index = slew_axis_->findAxisIndex(slew);
|
||||
size_t cap_index = cap_axis_->findAxisIndex(cap);
|
||||
size_t wave_index = slew_index * cap_axis_->size() + cap_index;
|
||||
FloatSeq *times = new FloatSeq;
|
||||
FloatSeq *currents = new FloatSeq;
|
||||
for (size_t i = 0; i <= voltage_waveform_step_count_; i++) {
|
||||
float volt = i * vdd_ / voltage_waveform_step_count_;
|
||||
float time = voltageTime(slew, cap, volt);
|
||||
float current = voltageCurrent(slew, cap, volt);
|
||||
times->push_back(time);
|
||||
currents->push_back(current);
|
||||
}
|
||||
TableAxisPtr time_axis = make_shared<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];
|
||||
}
|
||||
|
||||
|
|
@ -1701,7 +1749,9 @@ OutputWaveforms::timeCurrent(float slew,
|
|||
float cap,
|
||||
float time)
|
||||
{
|
||||
return waveformValue(slew, cap, time, current_waveforms_);
|
||||
// Current waveform is not monotonic, so use volt/current correspondence.
|
||||
float volt = timeVoltage(slew, cap, time);
|
||||
return voltageCurrent(slew, cap, volt);
|
||||
}
|
||||
|
||||
float
|
||||
|
|
@ -1709,7 +1759,81 @@ OutputWaveforms::timeVoltage(float slew,
|
|||
float cap,
|
||||
float time)
|
||||
{
|
||||
return waveformValue(slew, cap, time, voltage_waveforms_);
|
||||
size_t slew_index = slew_axis_->findAxisIndex(slew);
|
||||
size_t cap_index = cap_axis_->findAxisIndex(cap);
|
||||
size_t cap_count = cap_axis_->size();
|
||||
size_t wave_index00 = slew_index * cap_count + cap_index;
|
||||
size_t wave_index01 = slew_index * cap_count + (cap_index + 1);
|
||||
size_t wave_index10 = (slew_index + 1) * cap_count + cap_index;
|
||||
size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1);
|
||||
|
||||
size_t index1 = slew_index;
|
||||
size_t index2 = cap_index;
|
||||
double x1 = slew;
|
||||
double x2 = cap;
|
||||
double x1l = slew_axis_->axisValue(index1);
|
||||
double x1u = slew_axis_->axisValue(index1 + 1);
|
||||
double dx1 = (x1 - x1l) / (x1u - x1l);
|
||||
double x2l = cap_axis_->axisValue(index2);
|
||||
double x2u = cap_axis_->axisValue(index2 + 1);
|
||||
double dx2 = (x2 - x2l) / (x2u - x2l);
|
||||
|
||||
double v_lo = 0.0;
|
||||
double v_hi = vdd_;
|
||||
double v_mid = (v_hi + v_lo) * 0.5;
|
||||
double time_mid;
|
||||
while (v_hi - v_lo > .001) {
|
||||
time_mid = voltageTime1(v_mid, dx1, dx2, wave_index00, wave_index01,
|
||||
wave_index10, wave_index11);
|
||||
if (time > time_mid) {
|
||||
v_lo = v_mid;
|
||||
v_mid = (v_hi + v_lo) * 0.5;
|
||||
}
|
||||
else {
|
||||
v_hi = v_mid;
|
||||
v_mid = (v_hi + v_lo) * 0.5;
|
||||
}
|
||||
}
|
||||
return v_mid;
|
||||
}
|
||||
|
||||
double
|
||||
OutputWaveforms::voltageTime1(double volt,
|
||||
double dx1,
|
||||
double dx2,
|
||||
size_t wave_index00,
|
||||
size_t wave_index01,
|
||||
size_t wave_index10,
|
||||
size_t wave_index11)
|
||||
{
|
||||
double y00 = voltageTime2(volt, wave_index00);
|
||||
double y01 = voltageTime2(volt, wave_index01);
|
||||
double y10 = voltageTime2(volt, wave_index10);
|
||||
double y11 = voltageTime2(volt, wave_index11);
|
||||
double time
|
||||
= (1 - dx1) * (1 - dx2) * y00
|
||||
+ dx1 * (1 - dx2) * y10
|
||||
+ dx1 * dx2 * y11
|
||||
+ (1 - dx1) * dx2 * y01;
|
||||
return time;
|
||||
}
|
||||
|
||||
float
|
||||
OutputWaveforms::voltageTime2(float volt,
|
||||
size_t wave_index)
|
||||
{
|
||||
const Table1 *voltage_waveform = voltage_waveforms_[wave_index];
|
||||
const FloatSeq *voltages = voltage_waveform->values();
|
||||
size_t index1 = findValueIndex(volt, voltages);
|
||||
float volt_lo = (*voltages)[index1];
|
||||
float volt_hi = (*voltages)[index1 + 1];
|
||||
float dv = volt_hi - volt_lo;
|
||||
|
||||
const TableAxis *time_axis = voltage_waveform->axis1();
|
||||
float time_lo = time_axis->axisValue(index1);
|
||||
float time_hi = time_axis->axisValue(index1 + 1);
|
||||
float dt = time_hi - time_lo;
|
||||
return time_lo + dt * (volt - volt_lo) / dv;
|
||||
}
|
||||
|
||||
float
|
||||
|
|
@ -1773,14 +1897,11 @@ Table1
|
|||
OutputWaveforms::voltageWaveform(float slew,
|
||||
float cap)
|
||||
{
|
||||
float min_time, max_time;
|
||||
waveformMinMaxTime(slew, cap, voltage_waveforms_, min_time, max_time);
|
||||
float time_step = (max_time - min_time) / voltage_waveform_step_count_;
|
||||
FloatSeq *times = new FloatSeq;
|
||||
FloatSeq *volts = new FloatSeq;
|
||||
for (size_t i = 0; i < voltage_waveform_step_count_; i++) {
|
||||
float time = min_time + i * time_step;
|
||||
float volt = timeVoltage(slew, cap, time);
|
||||
for (size_t i = 0; i <= voltage_waveform_step_count_; i++) {
|
||||
float volt = i * vdd_ / voltage_waveform_step_count_;
|
||||
float time = voltageTime(slew, cap, volt);
|
||||
times->push_back(time);
|
||||
volts->push_back(volt);
|
||||
}
|
||||
|
|
@ -1788,42 +1909,21 @@ OutputWaveforms::voltageWaveform(float slew,
|
|||
return Table1(volts, time_axis);
|
||||
}
|
||||
|
||||
void
|
||||
OutputWaveforms::waveformMinMaxTime(float slew,
|
||||
float cap,
|
||||
Table1Seq &waveforms,
|
||||
// Return values.
|
||||
float &min_time,
|
||||
float &max_time)
|
||||
const Table1 *
|
||||
OutputWaveforms::voltageWaveformRaw(float slew,
|
||||
float cap)
|
||||
{
|
||||
size_t slew_index = slew_axis_->findAxisIndex(slew);
|
||||
size_t cap_index = cap_axis_->findAxisIndex(cap);
|
||||
size_t slew_index = slew_axis_->findAxisClosestIndex(slew);
|
||||
size_t cap_index = cap_axis_->findAxisClosestIndex(cap);
|
||||
size_t cap_count = cap_axis_->size();
|
||||
size_t wave_index00 = slew_index * cap_count + cap_index;
|
||||
size_t wave_index01 = slew_index * cap_count + (cap_index + 1);
|
||||
size_t wave_index10 = (slew_index + 1) * cap_count + cap_index;
|
||||
size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1);
|
||||
|
||||
const Table1 *waveform00 = waveforms[wave_index00];
|
||||
const Table1 *waveform01 = waveforms[wave_index01];
|
||||
const Table1 *waveform10 = waveforms[wave_index10];
|
||||
const Table1 *waveform11 = waveforms[wave_index11];
|
||||
|
||||
min_time = waveform00->axis1()->min();
|
||||
min_time = min(min_time, waveform01->axis1()->min());
|
||||
min_time = min(min_time, waveform10->axis1()->min());
|
||||
min_time = min(min_time, waveform11->axis1()->min());
|
||||
|
||||
max_time = waveform00->axis1()->max();
|
||||
max_time = max(max_time, waveform01->axis1()->max());
|
||||
max_time = max(max_time, waveform10->axis1()->max());
|
||||
max_time = max(max_time, waveform11->axis1()->max());
|
||||
size_t wave_index = slew_index * cap_count + cap_index;
|
||||
return voltage_waveforms_[wave_index];
|
||||
}
|
||||
|
||||
float
|
||||
OutputWaveforms::voltageTime(float slew,
|
||||
float cap,
|
||||
float volt)
|
||||
float cap,
|
||||
float volt)
|
||||
{
|
||||
size_t slew_index = slew_axis_->findAxisIndex(slew);
|
||||
size_t cap_index = cap_axis_->findAxisIndex(cap);
|
||||
|
|
@ -1845,33 +1945,109 @@ OutputWaveforms::voltageTime(float slew,
|
|||
double x2u = cap_axis_->axisValue(index2 + 1);
|
||||
double dx2 = (x2 - x2l) / (x2u - x2l);
|
||||
|
||||
double y00 = voltageTime1(volt, wave_index00);
|
||||
double y01 = voltageTime1(volt, wave_index01);
|
||||
double y10 = voltageTime1(volt, wave_index10);
|
||||
double y11 = voltageTime1(volt, wave_index11);
|
||||
double time
|
||||
= (1 - dx1) * (1 - dx2) * y00
|
||||
+ dx1 * (1 - dx2) * y10
|
||||
+ dx1 * dx2 * y11
|
||||
+ (1 - dx1) * dx2 * y01;
|
||||
double time = voltageTime1(volt, dx1, dx2, wave_index00, wave_index01,
|
||||
wave_index10, wave_index11);
|
||||
return time;
|
||||
}
|
||||
|
||||
float
|
||||
OutputWaveforms::voltageTime1(float voltage,
|
||||
size_t wave_index)
|
||||
OutputWaveforms::beginTime(float slew,
|
||||
float cap)
|
||||
{
|
||||
FloatSeq *voltage_times = voltage_times_[wave_index];
|
||||
float volt_step = vdd_ / voltage_waveform_step_count_;
|
||||
size_t volt_idx = voltage / volt_step;
|
||||
if (volt_idx >= voltage_times->size() - 1)
|
||||
return (*voltage_times)[voltage_times->size() - 1];
|
||||
else {
|
||||
double time0 = (*voltage_times)[volt_idx];
|
||||
double time1 = (*voltage_times)[volt_idx + 1];
|
||||
double time = time0 + (time1 - time0) * (voltage - volt_step * volt_idx);
|
||||
return time;
|
||||
return beginEndTime(slew, cap, true);
|
||||
}
|
||||
|
||||
float
|
||||
OutputWaveforms::endTime(float slew,
|
||||
float cap)
|
||||
{
|
||||
return beginEndTime(slew, cap, false);
|
||||
}
|
||||
|
||||
float
|
||||
OutputWaveforms::beginEndTime(float slew,
|
||||
float cap,
|
||||
bool begin)
|
||||
{
|
||||
size_t slew_index = slew_axis_->findAxisIndex(slew);
|
||||
size_t cap_index = cap_axis_->findAxisIndex(cap);
|
||||
size_t cap_count = cap_axis_->size();
|
||||
size_t wave_index00 = slew_index * cap_count + cap_index;
|
||||
size_t wave_index01 = slew_index * cap_count + (cap_index + 1);
|
||||
size_t wave_index10 = (slew_index + 1) * cap_count + cap_index;
|
||||
size_t wave_index11 = (slew_index + 1) * cap_count + (cap_index + 1);
|
||||
|
||||
const Table1 *waveform00 = current_waveforms_[wave_index00];
|
||||
const Table1 *waveform01 = current_waveforms_[wave_index01];
|
||||
const Table1 *waveform10 = current_waveforms_[wave_index10];
|
||||
const Table1 *waveform11 = current_waveforms_[wave_index11];
|
||||
|
||||
// Interpolate waveform samples at voltage steps.
|
||||
size_t index1 = slew_index;
|
||||
size_t index2 = cap_index;
|
||||
float x1 = slew;
|
||||
float x2 = cap;
|
||||
float x1l = slew_axis_->axisValue(index1);
|
||||
float x1u = slew_axis_->axisValue(index1 + 1);
|
||||
float dx1 = (x1 - x1l) / (x1u - x1l);
|
||||
float x2l = cap_axis_->axisValue(index2);
|
||||
float x2u = cap_axis_->axisValue(index2 + 1);
|
||||
float dx2 = (x2 - x2l) / (x2u - x2l);
|
||||
|
||||
float y00, y01, y10, y11;
|
||||
if (begin) {
|
||||
y00 = waveform00->axis1()->min();
|
||||
y01 = waveform01->axis1()->min();
|
||||
y10 = waveform10->axis1()->min();
|
||||
y11 = waveform11->axis1()->min();
|
||||
}
|
||||
else {
|
||||
y00 = waveform00->axis1()->max();
|
||||
y01 = waveform01->axis1()->max();
|
||||
y10 = waveform10->axis1()->max();
|
||||
y11 = waveform11->axis1()->max();
|
||||
}
|
||||
|
||||
float wave_value
|
||||
= (1 - dx1) * (1 - dx2) * y00
|
||||
+ dx1 * (1 - dx2) * y10
|
||||
+ dx1 * dx2 * y11
|
||||
+ (1 - dx1) * dx2 * y01;
|
||||
return wave_value;
|
||||
}
|
||||
|
||||
Table1
|
||||
OutputWaveforms::voltageCurrentWaveform(float slew,
|
||||
float cap)
|
||||
{
|
||||
FloatSeq *volts = new FloatSeq;
|
||||
FloatSeq *currents = new FloatSeq;
|
||||
for (size_t i = 0; i < voltage_waveform_step_count_; i++) {
|
||||
float volt = i * vdd_ / voltage_waveform_step_count_;
|
||||
float current = voltageCurrent(slew, cap, volt);
|
||||
volts->push_back(volt);
|
||||
currents->push_back(current);
|
||||
}
|
||||
TableAxisPtr volt_axis =
|
||||
make_shared<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]);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include "TimingRole.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "DcalcAnalysisPt.hh"
|
||||
#include "TableModel.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -316,13 +318,13 @@ TimingArcSet::sense() const
|
|||
return attrs_->timingSense();
|
||||
}
|
||||
|
||||
RiseFall *
|
||||
const RiseFall *
|
||||
TimingArcSet::isRisingFallingEdge() const
|
||||
{
|
||||
int arc_count = arcs_.size();
|
||||
if (arc_count == 2) {
|
||||
RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall();
|
||||
RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall();
|
||||
const RiseFall *from_rf1 = arcs_[0]->fromEdge()->asRiseFall();
|
||||
const RiseFall *from_rf2 = arcs_[1]->fromEdge()->asRiseFall();
|
||||
if (from_rf1 == from_rf2)
|
||||
return from_rf1;
|
||||
}
|
||||
|
|
@ -545,18 +547,42 @@ TimingArc::~TimingArc()
|
|||
delete scaled_models_;
|
||||
}
|
||||
|
||||
TimingModel *
|
||||
TimingArc::model(const OperatingConditions *op_cond) const
|
||||
GateTimingModel *
|
||||
TimingArc::gateModel(const DcalcAnalysisPt *dcalc_ap) const
|
||||
{
|
||||
if (scaled_models_) {
|
||||
TimingModel *model = scaled_models_->findKey(op_cond);
|
||||
if (model)
|
||||
return model;
|
||||
else
|
||||
return model_;
|
||||
return dynamic_cast<GateTimingModel*>(model(dcalc_ap));
|
||||
}
|
||||
|
||||
GateTableModel *
|
||||
TimingArc::gateTableModel() const
|
||||
{
|
||||
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 model_;
|
||||
return corner_arc->model();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -320,8 +320,9 @@ public:
|
|||
int from() const { return from_; }
|
||||
int to() const { return to_; }
|
||||
ConcretePortSeq &members() { return members_; }
|
||||
const ConcretePortSeq &members() const { return members_; }
|
||||
void setDirection(PortDirection *direction);
|
||||
PortDirection *direction() { return direction_; }
|
||||
PortDirection *direction() const { return direction_; }
|
||||
|
||||
private:
|
||||
int from_;
|
||||
|
|
@ -385,9 +386,7 @@ ConcreteCell::groupBusPorts(const char bus_brkt_left,
|
|||
}
|
||||
|
||||
// Make the bus ports.
|
||||
for (auto name_bus : bus_map) {
|
||||
string bus_name = name_bus.first;
|
||||
BusPort &bus_port = name_bus.second;
|
||||
for (const auto& [bus_name, bus_port] : bus_map) {
|
||||
int from = bus_port.from();
|
||||
int to = bus_port.to();
|
||||
size_t size = to - from + 1;
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@
|
|||
|
||||
namespace eval sta {
|
||||
|
||||
define_cmd_args "link_design" {[top_cell_name]}
|
||||
define_cmd_args "link_design" {[-no_black_boxes]\
|
||||
[top_cell_name]}
|
||||
|
||||
proc_redirect link_design {
|
||||
variable current_design_name
|
||||
|
||||
parse_key_args "link_design" args keys {} flags {-no_black_boxes}
|
||||
check_argc_eq0or1 "link_design" $args
|
||||
if { $args == "" } {
|
||||
set top_cell_name ""
|
||||
|
|
@ -37,7 +39,11 @@ proc_redirect link_design {
|
|||
set top_cell_name $current_design_name
|
||||
}
|
||||
}
|
||||
link_design_cmd $top_cell_name
|
||||
set make_black_boxes 1
|
||||
if { [info exists flags(-no_black_boxes)] } {
|
||||
set make_black_boxes 0
|
||||
}
|
||||
link_design_cmd $top_cell_name $make_black_boxes
|
||||
}
|
||||
|
||||
# sta namespace end
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -26,9 +26,11 @@ proc_redirect report_instance {
|
|||
check_argc_eq1 "report_instance" $args
|
||||
|
||||
if { [info exists flags(-connections)] } {
|
||||
# deprecated 2024-01-17
|
||||
sta_warn 233 "report_instance -connections is deprecated."
|
||||
}
|
||||
if { [info exists flags(-verbose)] } {
|
||||
# deprecated 2024-01-17
|
||||
sta_warn 234 "report_instance -verbose is deprecated."
|
||||
}
|
||||
set instance_path [lindex $args 0]
|
||||
|
|
@ -183,12 +185,15 @@ proc_redirect report_net {
|
|||
check_argc_eq1 "report_net" $args
|
||||
|
||||
if { [info exists flags(-connections)] } {
|
||||
# deprecated 2024-01-17
|
||||
sta_warn 235 "report_net -connections is deprecated."
|
||||
}
|
||||
if { [info exists flags(-verbose)] } {
|
||||
# deprecated 2024-01-17
|
||||
sta_warn 236 "report_net -verbose is deprecated."
|
||||
}
|
||||
if { [info exists flags(-hier_pins)] } {
|
||||
# deprecated 2024-01-17
|
||||
sta_warn 237 "report_net -hier_pins is deprecated."
|
||||
}
|
||||
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
%module NetworkEdit
|
||||
|
||||
%{
|
||||
|
||||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2024, Parallax Software, Inc.
|
||||
//
|
||||
|
|
@ -18,23 +14,23 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
%module NetworkEdit
|
||||
|
||||
%{
|
||||
using sta::Cell;
|
||||
using sta::Instance;
|
||||
using sta::Net;
|
||||
using sta::Port;
|
||||
using sta::Pin;
|
||||
using sta::NetworkEdit;
|
||||
using sta::cmdEditNetwork;
|
||||
|
||||
namespace sta {
|
||||
NetworkEdit *
|
||||
cmdEditNetwork();
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SWIG type definitions.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// C++ functions visible as TCL functions.
|
||||
|
|
@ -68,7 +64,7 @@ Net *
|
|||
make_net_cmd(const char *name,
|
||||
Instance *parent)
|
||||
{
|
||||
Net *net = cmdEditNetwork()->makeNet(name, parent);
|
||||
Net *net = sta::cmdEditNetwork()->makeNet(name, parent);
|
||||
// Sta notification unnecessary.
|
||||
return net;
|
||||
}
|
||||
|
|
@ -133,38 +133,6 @@ proc parse_connect_pin { arg } {
|
|||
return [list $inst $port]
|
||||
}
|
||||
|
||||
proc connect_pins { net pins } {
|
||||
sta_warn 251 "connect_pins is deprecated. Use connect_pin."
|
||||
# Visit the pins to make sure command will succeed.
|
||||
set insts_ports [parse_connect_pins $pins]
|
||||
if { $insts_ports == 0 } {
|
||||
return 0
|
||||
}
|
||||
set net [get_net_arg "net" $net]
|
||||
if { $net == "NULL" } {
|
||||
return 0
|
||||
}
|
||||
foreach {inst port} $insts_ports {
|
||||
connect_pin_cmd $inst $port $net
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
proc parse_connect_pins { arg } {
|
||||
set path_regexp [path_regexp]
|
||||
set inst_ports {}
|
||||
# Copy backslashes that will be removed by foreach.
|
||||
set arg [string map {\\ \\\\} $arg]
|
||||
foreach obj $arg {
|
||||
set inst_port [parse_connect_pin $obj]
|
||||
if { $inst_port == 0 } {
|
||||
return 0
|
||||
}
|
||||
set inst_ports [concat $inst_ports $inst_port]
|
||||
}
|
||||
return $inst_ports
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "disconnect_pin" {net -all|pin}
|
||||
|
|
@ -246,10 +246,8 @@ ConcretePiElmore::unannotatedLoads(const Pin *drvr_pin,
|
|||
const Parasitics *parasitics) const
|
||||
{
|
||||
PinSet loads = parasitics->loads(drvr_pin);
|
||||
for (auto pin_elmore : loads_) {
|
||||
const Pin *load = pin_elmore.first;
|
||||
for (const auto [load, elmore] : loads_)
|
||||
loads.erase(load);
|
||||
}
|
||||
return loads;
|
||||
}
|
||||
|
||||
|
|
@ -371,10 +369,8 @@ ConcretePiPoleResidue::unannotatedLoads(const Pin *drvr_pin,
|
|||
const Parasitics *parasitics) const
|
||||
{
|
||||
PinSet loads = parasitics->loads(drvr_pin);
|
||||
for (auto pin_pole_residue : load_pole_residue_) {
|
||||
const Pin *load = pin_pole_residue.first;
|
||||
for (const auto& [load, pole_residue] : load_pole_residue_)
|
||||
loads.erase(load);
|
||||
}
|
||||
return loads;
|
||||
}
|
||||
|
||||
|
|
@ -395,6 +391,7 @@ ConcreteParasiticNode::ConcreteParasiticNode(const Pin *pin,
|
|||
bool is_external) :
|
||||
is_net_(false),
|
||||
is_external_(is_external),
|
||||
id_(0),
|
||||
cap_(0.0)
|
||||
{
|
||||
net_pin_.pin_ = pin;
|
||||
|
|
@ -506,14 +503,10 @@ ConcreteParasiticNetwork::~ConcreteParasiticNetwork()
|
|||
void
|
||||
ConcreteParasiticNetwork::deleteNodes()
|
||||
{
|
||||
for (auto id_node : sub_nodes_) {
|
||||
ConcreteParasiticNode *node = id_node.second;
|
||||
for (const auto& [id, node] : sub_nodes_)
|
||||
delete node;
|
||||
}
|
||||
for (auto pin_node : pin_nodes_) {
|
||||
ConcreteParasiticNode *node = pin_node.second;
|
||||
for (const auto& [pin, node] : pin_nodes_)
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -547,14 +540,10 @@ ParasiticNodeSeq
|
|||
ConcreteParasiticNetwork::nodes() const
|
||||
{
|
||||
ParasiticNodeSeq nodes;
|
||||
for (auto pin_node : pin_nodes_) {
|
||||
ParasiticNode *node = pin_node.second;
|
||||
for (const auto [pin, node] : pin_nodes_)
|
||||
nodes.push_back(node);
|
||||
}
|
||||
for (auto id_node : sub_nodes_) {
|
||||
ParasiticNode *node = id_node.second;
|
||||
for (const auto& [id, node] : sub_nodes_)
|
||||
nodes.push_back(node);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
|
@ -562,14 +551,12 @@ float
|
|||
ConcreteParasiticNetwork::capacitance() const
|
||||
{
|
||||
float cap = 0.0;
|
||||
for (auto id_node : sub_nodes_) {
|
||||
ConcreteParasiticNode *node = id_node.second;
|
||||
for (const auto& [id, node] : sub_nodes_) {
|
||||
if (!node->isExternal())
|
||||
cap += node->capacitance();
|
||||
}
|
||||
|
||||
for (auto pin_node : pin_nodes_) {
|
||||
ConcreteParasiticNode *node = pin_node.second;
|
||||
for (const auto [pin, node] : pin_nodes_) {
|
||||
if (!node->isExternal())
|
||||
cap += node->capacitance();
|
||||
}
|
||||
|
|
@ -617,7 +604,8 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net,
|
|||
if (id_node == sub_nodes_.end()) {
|
||||
node = new ConcreteParasiticNode(net, id, net != net_);
|
||||
sub_nodes_[net_id] = node;
|
||||
max_node_id_ = max((int) max_node_id_, id);
|
||||
if (net == net_)
|
||||
max_node_id_ = max((int) max_node_id_, id);
|
||||
}
|
||||
else
|
||||
node = id_node->second;
|
||||
|
|
@ -790,8 +778,7 @@ ConcreteParasitics::deleteParasitics()
|
|||
{
|
||||
int ap_count = corners_->parasiticAnalysisPtCount();
|
||||
int ap_rf_count = ap_count * RiseFall::index_count;
|
||||
for (auto drvr_parasitics : drvr_parasitic_map_) {
|
||||
ConcreteParasitic **parasitics = drvr_parasitics.second;
|
||||
for (const auto [drvr, parasitics] : drvr_parasitic_map_) {
|
||||
if (parasitics) {
|
||||
for (int i = 0; i < ap_rf_count; i++)
|
||||
delete parasitics[i];
|
||||
|
|
@ -800,8 +787,7 @@ ConcreteParasitics::deleteParasitics()
|
|||
}
|
||||
drvr_parasitic_map_.clear();
|
||||
|
||||
for (auto net_parasitics : parasitic_network_map_) {
|
||||
ConcreteParasiticNetwork **parasitics = net_parasitics.second;
|
||||
for (const auto [net, parasitics] : parasitic_network_map_) {
|
||||
if (parasitics) {
|
||||
for (int i = 0; i < ap_count; i++)
|
||||
delete parasitics[i];
|
||||
|
|
@ -817,8 +803,8 @@ ConcreteParasitics::deleteParasitics(const Pin *drvr_pin,
|
|||
{
|
||||
ConcreteParasitic **parasitics = drvr_parasitic_map_[drvr_pin];
|
||||
if (parasitics) {
|
||||
for (auto tr : RiseFall::range()) {
|
||||
int ap_rf_index = parasiticAnalysisPtIndex(ap, tr);
|
||||
for (auto rf : RiseFall::range()) {
|
||||
int ap_rf_index = parasiticAnalysisPtIndex(ap, rf);
|
||||
delete parasitics[ap_rf_index];
|
||||
parasitics[ap_rf_index] = nullptr;
|
||||
}
|
||||
|
|
@ -1247,8 +1233,10 @@ ConcreteParasitics::makeParasiticNetwork(const Net *net,
|
|||
ConcreteParasiticNetwork *parasitic = parasitics[ap_index];
|
||||
if (parasitic) {
|
||||
delete parasitic;
|
||||
for (const Pin *drvr_pin : *network_->drivers(net))
|
||||
deleteParasitics(drvr_pin, ap);
|
||||
if (net) {
|
||||
for (const Pin *drvr_pin : *network_->drivers(net))
|
||||
deleteParasitics(drvr_pin, ap);
|
||||
}
|
||||
}
|
||||
parasitic = new ConcreteParasiticNetwork(net, includes_pin_caps, network_);
|
||||
parasitics[ap_index] = parasitic;
|
||||
|
|
@ -1446,6 +1434,14 @@ ConcreteParasitics::net(const ParasiticNode *node,
|
|||
return cnode->net(network);
|
||||
}
|
||||
|
||||
unsigned
|
||||
ConcreteParasitics::netId(const ParasiticNode *node) const
|
||||
{
|
||||
const ConcreteParasiticNode *cnode =
|
||||
static_cast<const ConcreteParasiticNode*>(node);
|
||||
return cnode->id();
|
||||
}
|
||||
|
||||
bool
|
||||
ConcreteParasitics::isExternal(const ParasiticNode *node) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ public:
|
|||
const Pin *pin(const ParasiticNode *node) const override;
|
||||
const Net *net(const ParasiticNode *node,
|
||||
const Network *network) const override;
|
||||
unsigned netId(const ParasiticNode *node) const override;
|
||||
bool isExternal(const ParasiticNode *node) const override;
|
||||
float nodeGndCap(const ParasiticNode *node) const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ typedef std::map<const Pin*, float> ConcreteElmoreLoadMap;
|
|||
typedef std::map<const Pin*, ConcretePoleResidue> ConcretePoleResidueMap;
|
||||
typedef std::map<NetIdPair,ConcreteParasiticNode*,
|
||||
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<ParasiticResistor*> ParasiticResistorSet;
|
||||
typedef std::vector<ParasiticResistor*> ParasiticResistorSeq;
|
||||
|
|
@ -262,6 +262,7 @@ public:
|
|||
float capacitance() const { return cap_; }
|
||||
const char *name(const Network *network) const;
|
||||
const Net *net(const Network *network) const;
|
||||
unsigned id() const { return id_; }
|
||||
bool isExternal() const { return is_external_; }
|
||||
const Pin *pin() const;
|
||||
void incrCapacitance(float cap);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "Wireload.hh"
|
||||
#include "Liberty.hh"
|
||||
#include "PortDirection.hh"
|
||||
#include "Network.hh"
|
||||
#include "Sdc.hh"
|
||||
#include "Parasitics.hh"
|
||||
|
|
@ -147,7 +148,7 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
|
|||
bool &elmore_use_load_cap)
|
||||
{
|
||||
if (wireload_res == 0.0
|
||||
|| fanout == 0) {
|
||||
|| fanout == 0.0) {
|
||||
// No resistance, so load is capacitance only.
|
||||
c2 = wireload_cap + net_pin_cap;
|
||||
rpi = 0.0;
|
||||
|
|
@ -168,22 +169,19 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
|
|||
network_->connectedPinIterator(drvr_pin);
|
||||
while (load_iter->hasNext()) {
|
||||
const Pin *load_pin = load_iter->next();
|
||||
Port *port = network_->port(load_pin);
|
||||
double cap = 0.0;
|
||||
// Bidirects don't count themselves as loads.
|
||||
if (load_pin != drvr_pin && network_->isLoad(load_pin)) {
|
||||
Port *port = network_->port(load_pin);
|
||||
double load_cap = 0.0;
|
||||
if (network_->isLeaf(load_pin))
|
||||
load_cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max);
|
||||
else if (network_->isTopLevelPort(load_pin))
|
||||
load_cap = sdc_->portExtCap(port, rf, corner, min_max);
|
||||
else
|
||||
report_->critical(1050, "load pin not leaf or top level");
|
||||
double cap = load_cap + cap_fanout;
|
||||
double y2_ = res_fanout * cap * cap;
|
||||
y1 += cap;
|
||||
y2 += -y2_;
|
||||
y3 += y2_ * res_fanout * cap;
|
||||
}
|
||||
if (load_pin == drvr_pin)
|
||||
cap = sdc_->portExtCap(port, rf, corner, min_max);
|
||||
else if (network_->isLeaf(load_pin))
|
||||
cap = sdc_->pinCapacitance(load_pin, rf, corner, min_max) + cap_fanout;
|
||||
else if (network_->isTopLevelPort(load_pin))
|
||||
cap = sdc_->portExtCap(port, rf, corner, min_max) + cap_fanout;
|
||||
double y2_ = res_fanout * cap * cap;
|
||||
y1 += cap;
|
||||
y2 += -y2_;
|
||||
y3 += y2_ * res_fanout * cap;
|
||||
}
|
||||
delete load_iter;
|
||||
|
||||
|
|
@ -196,6 +194,8 @@ EstimateParasitics::estimatePiElmoreBalanced(const Pin *drvr_pin,
|
|||
else {
|
||||
c1 = static_cast<float>(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));
|
||||
}
|
||||
elmore_res = static_cast<float>(res_fanout);
|
||||
|
|
|
|||
|
|
@ -203,29 +203,32 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin,
|
|||
const MinMax *min_max,
|
||||
const ParasiticAnalysisPt *ap)
|
||||
{
|
||||
Parasitic *parasitic = nullptr;
|
||||
const Net *net = findParasiticNet(drvr_pin);
|
||||
Parasitic *parasitic = makeParasiticNetwork(net, false, ap);
|
||||
const OperatingConditions *op_cond = sdc_->operatingConditions(min_max);
|
||||
float wireload_cap, wireload_res;
|
||||
wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res);
|
||||
if (net) {
|
||||
parasitic = makeParasiticNetwork(net, false, ap);
|
||||
const OperatingConditions *op_cond = sdc_->operatingConditions(min_max);
|
||||
float wireload_cap, wireload_res;
|
||||
wireload->findWireload(fanout, op_cond, wireload_cap, wireload_res);
|
||||
|
||||
WireloadTree tree = WireloadTree::balanced;
|
||||
if (op_cond)
|
||||
tree = op_cond->wireloadTree();
|
||||
switch (tree) {
|
||||
case WireloadTree::worst_case:
|
||||
makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
case WireloadTree::balanced:
|
||||
makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
case WireloadTree::best_case:
|
||||
case WireloadTree::unknown:
|
||||
makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
WireloadTree tree = WireloadTree::balanced;
|
||||
if (op_cond)
|
||||
tree = op_cond->wireloadTree();
|
||||
switch (tree) {
|
||||
case WireloadTree::worst_case:
|
||||
makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
case WireloadTree::balanced:
|
||||
makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
case WireloadTree::best_case:
|
||||
case WireloadTree::unknown:
|
||||
makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parasitic;
|
||||
}
|
||||
|
|
@ -324,4 +327,38 @@ ParasiticAnalysisPt::setCouplingCapFactor(float factor)
|
|||
coupling_cap_factor_ = factor;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
ParasiticNodeLess::ParasiticNodeLess(const Parasitics *parasitics,
|
||||
const Network *network) :
|
||||
parasitics_(parasitics),
|
||||
network_(network)
|
||||
{
|
||||
}
|
||||
|
||||
ParasiticNodeLess::ParasiticNodeLess(const ParasiticNodeLess &less) :
|
||||
parasitics_(less.parasitics_),
|
||||
network_(less.network_)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ParasiticNodeLess::operator()(const ParasiticNode *node1,
|
||||
const ParasiticNode *node2) const
|
||||
{
|
||||
const Pin *pin1 = parasitics_->pin(node1);
|
||||
const Pin *pin2 = parasitics_->pin(node2);
|
||||
const Net *net1 = parasitics_->net(node1, network_);
|
||||
const Net *net2 = parasitics_->net(node2, network_);
|
||||
unsigned id1 = parasitics_->netId(node1);
|
||||
unsigned id2 = parasitics_->netId(node2);
|
||||
return (pin1 == nullptr && pin2)
|
||||
|| (pin1 && pin2
|
||||
&& network_->id(pin1) < network_->id(pin2))
|
||||
|| (pin1 == nullptr && pin2 == nullptr
|
||||
&& (network_->id(net1) < network_->id(net2)
|
||||
|| (net1 == net2
|
||||
&& id1 < id2)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
%module parasitics
|
||||
|
||||
%{
|
||||
|
||||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2024, Parallax Software, Inc.
|
||||
//
|
||||
|
|
@ -18,6 +14,9 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
%module parasitics
|
||||
|
||||
%{
|
||||
#include "Sta.hh"
|
||||
|
||||
using sta::Sta;
|
||||
|
|
|
|||
|
|
@ -37,16 +37,20 @@ proc_redirect read_spef {
|
|||
|
||||
set reduce [info exists flags(-reduce)]
|
||||
if { [info exists flags(-quiet)] } {
|
||||
# deprecated 2024-02-08
|
||||
sta_warn 272 "read_spef -quiet is deprecated."
|
||||
}
|
||||
if { [info exists keys(-reduce_to)] } {
|
||||
# deprecated 2024-02-08
|
||||
sta_warn 273 "read_spef -reduce_to is deprecated. Use -reduce instead."
|
||||
set reduce 1
|
||||
}
|
||||
if { [info exists flags(-delete_after_reduce)] } {
|
||||
# deprecated 2024-02-08
|
||||
sta_warn 274 "read_spef -delete_after_reduce is deprecated."
|
||||
}
|
||||
if { [info exists flags(-save)] } {
|
||||
# deprecated 2024-02-08
|
||||
sta_warn 275 "read_spef -save is deprecated."
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin,
|
|||
&& resistor != from_res) {
|
||||
if (isVisited(onode)) {
|
||||
// Resistor loop.
|
||||
debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %lu",
|
||||
debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %zu",
|
||||
parasitics_->id(resistor));
|
||||
markLoopResistor(resistor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,14 +80,14 @@ ReportParasiticAnnotation::report()
|
|||
void
|
||||
ReportParasiticAnnotation::reportAnnotationCounts()
|
||||
{
|
||||
report_->reportLine("Found %lu unannotated drivers.", unannotated_.size());
|
||||
report_->reportLine("Found %zu unannotated drivers.", unannotated_.size());
|
||||
if (report_unannotated_) {
|
||||
sort(unannotated_, PinPathNameLess(network_));
|
||||
for (const Pin *drvr_pin : unannotated_)
|
||||
report_->reportLine(" %s", network_->pathName(drvr_pin));
|
||||
}
|
||||
|
||||
report_->reportLine("Found %lu partially unannotated drivers.",
|
||||
report_->reportLine("Found %zu partially unannotated drivers.",
|
||||
partially_annotated_.size());
|
||||
if (report_unannotated_) {
|
||||
sort(partially_annotated_, PinPathNameLess(network_));
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ readSpefFile(const char *filename,
|
|||
StaState *sta)
|
||||
{
|
||||
bool success = false;
|
||||
const ArcDelayCalc *arc_delay_calc = sta->arcDelayCalc();
|
||||
if (reduce && !arc_delay_calc->reduceSupported()) {
|
||||
sta->report()->warn(1658, "Delay calculator %s does not support reduction.",
|
||||
arc_delay_calc->name());
|
||||
reduce = false;
|
||||
}
|
||||
// Use zlib to uncompress gzip'd files automagically.
|
||||
gzFile stream = gzopen(filename, "rb");
|
||||
if (stream) {
|
||||
|
|
@ -120,10 +126,8 @@ SpefReader::~SpefReader()
|
|||
design_flow_ = nullptr;
|
||||
}
|
||||
|
||||
for (auto index_name : name_map_) {
|
||||
char *name = index_name.second;
|
||||
for (const auto [index, name] : name_map_)
|
||||
stringDelete(name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -496,7 +500,9 @@ SpefReader::findParasiticNode(char *name,
|
|||
int id = atoi(id_str);
|
||||
if (local_only
|
||||
&& !network_->isConnected(net, net_))
|
||||
warn(1653, "%s not connected to net %s.", name, network_->pathName(net_));
|
||||
warn(1653, "%s not connected to net %s.",
|
||||
name,
|
||||
network_->pathName(net_));
|
||||
return parasitics_->ensureParasiticNode(parasitic_, net, id, network_);
|
||||
}
|
||||
else
|
||||
|
|
|
|||
113
power/Power.cc
113
power/Power.cc
|
|
@ -19,6 +19,7 @@
|
|||
#include <algorithm> // max
|
||||
#include <cmath> // abs
|
||||
|
||||
#include "cudd.h"
|
||||
#include "Debug.hh"
|
||||
#include "EnumNameMap.hh"
|
||||
#include "Hash.hh"
|
||||
|
|
@ -502,8 +503,6 @@ Power::clockGatePins(const Instance *inst,
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#if CUDD
|
||||
|
||||
PwrActivity
|
||||
Power::evalActivity(FuncExpr *expr,
|
||||
const Instance *inst)
|
||||
|
|
@ -586,12 +585,10 @@ Power::evalBddActivity(DdNode *bdd,
|
|||
const Instance *inst)
|
||||
{
|
||||
float activity = 0.0;
|
||||
for (auto port_var : bdd_.portVarMap()) {
|
||||
const LibertyPort *port = port_var.first;
|
||||
for (const auto [port, var_node] : bdd_.portVarMap()) {
|
||||
const Pin *pin = findLinkPin(inst, port);
|
||||
if (pin) {
|
||||
PwrActivity var_activity = findActivity(pin);
|
||||
DdNode *var_node = port_var.second;
|
||||
unsigned int var_index = Cudd_NodeReadIndex(var_node);
|
||||
DdNode *diff = Cudd_bddBooleanDiff(bdd_.cuddMgr(), bdd, var_index);
|
||||
Cudd_Ref(diff);
|
||||
|
|
@ -611,112 +608,6 @@ Power::evalBddActivity(DdNode *bdd,
|
|||
return activity;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
PwrActivity
|
||||
Power::evalActivity(FuncExpr *expr,
|
||||
const Instance *inst)
|
||||
{
|
||||
return evalActivity(expr, inst, nullptr, true);
|
||||
}
|
||||
|
||||
// Eval activity thru expr.
|
||||
// With cofactor_port eval the positive/negative cofactor of expr wrt cofactor_port.
|
||||
PwrActivity
|
||||
Power::evalActivity(FuncExpr *expr,
|
||||
const Instance *inst,
|
||||
const LibertyPort *cofactor_port,
|
||||
bool cofactor_positive)
|
||||
{
|
||||
switch (expr->op()) {
|
||||
case FuncExpr::op_port: {
|
||||
LibertyPort *port = expr->port();
|
||||
if (port == cofactor_port) {
|
||||
if (cofactor_positive)
|
||||
return PwrActivity(0.0, 1.0, PwrActivityOrigin::constant);
|
||||
else
|
||||
return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant);
|
||||
}
|
||||
if (port->direction()->isInternal())
|
||||
return findSeqActivity(inst, port);
|
||||
else {
|
||||
Pin *pin = findLinkPin(inst, port);
|
||||
if (pin) {
|
||||
PwrActivity activity = findActivity(pin);
|
||||
activity.setOrigin(PwrActivityOrigin::propagated);
|
||||
return activity;
|
||||
}
|
||||
}
|
||||
return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant);
|
||||
}
|
||||
case FuncExpr::op_not: {
|
||||
PwrActivity activity1 = evalActivity(expr->left(), inst,
|
||||
cofactor_port, cofactor_positive);
|
||||
return PwrActivity(activity1.activity(),
|
||||
1.0 - activity1.duty(),
|
||||
PwrActivityOrigin::propagated);
|
||||
}
|
||||
case FuncExpr::op_or: {
|
||||
PwrActivity activity1 = evalActivity(expr->left(), inst,
|
||||
cofactor_port, cofactor_positive);
|
||||
PwrActivity activity2 = evalActivity(expr->right(), inst,
|
||||
cofactor_port, cofactor_positive);
|
||||
float p1 = 1.0 - activity1.duty();
|
||||
float p2 = 1.0 - activity2.duty();
|
||||
return PwrActivity(activity1.activity() * p2 + activity2.activity() * p1,
|
||||
// d1 + d2 - d1 * d2
|
||||
1.0 - p1 * p2,
|
||||
PwrActivityOrigin::propagated);
|
||||
}
|
||||
case FuncExpr::op_and: {
|
||||
PwrActivity activity1 = evalActivity(expr->left(), inst,
|
||||
cofactor_port, cofactor_positive);
|
||||
PwrActivity activity2 = evalActivity(expr->right(), inst,
|
||||
cofactor_port, cofactor_positive);
|
||||
float p1 = activity1.duty();
|
||||
float p2 = activity2.duty();
|
||||
return PwrActivity(activity1.activity() * p2 + activity2.activity() * p1,
|
||||
p1 * p2,
|
||||
PwrActivityOrigin::propagated);
|
||||
}
|
||||
case FuncExpr::op_xor: {
|
||||
PwrActivity activity1 = evalActivity(expr->left(), inst,
|
||||
cofactor_port, cofactor_positive);
|
||||
PwrActivity activity2 = evalActivity(expr->right(), inst,
|
||||
cofactor_port, cofactor_positive);
|
||||
float d1 = activity1.duty();
|
||||
float d2 = activity2.duty();
|
||||
float p1 = d1 * (1.0 - d2);
|
||||
float p2 = (1.0 - d1) * d2;
|
||||
return PwrActivity(activity1.activity() + activity2.activity(),
|
||||
p1 + p2,
|
||||
PwrActivityOrigin::propagated);
|
||||
}
|
||||
case FuncExpr::op_one:
|
||||
return PwrActivity(0.0, 1.0, PwrActivityOrigin::constant);
|
||||
case FuncExpr::op_zero:
|
||||
return PwrActivity(0.0, 0.0, PwrActivityOrigin::constant);
|
||||
}
|
||||
return PwrActivity();
|
||||
}
|
||||
|
||||
// Eval activity of difference(expr) wrt cofactor port.
|
||||
float
|
||||
Power::evalDiffDuty(FuncExpr *expr,
|
||||
LibertyPort *cofactor_port,
|
||||
const Instance *inst)
|
||||
{
|
||||
// Activity of positive/negative cofactors.
|
||||
PwrActivity pos = evalActivity(expr, inst, cofactor_port, true);
|
||||
PwrActivity neg = evalActivity(expr, inst, cofactor_port, false);
|
||||
// difference = xor(pos, neg).
|
||||
float p1 = pos.duty() * (1.0 - neg.duty());
|
||||
float p2 = neg.duty() * (1.0 - pos.duty());
|
||||
return p1 + p2;
|
||||
}
|
||||
|
||||
#endif // CUDD
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
%module power
|
||||
|
||||
%{
|
||||
|
||||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2024, Parallax Software, Inc.
|
||||
//
|
||||
|
|
@ -18,6 +14,9 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
%module power
|
||||
|
||||
%{
|
||||
#include "Sta.hh"
|
||||
#include "power/Power.hh"
|
||||
#include "power/VcdReader.hh"
|
||||
|
|
@ -60,7 +59,8 @@ instance_power(Instance *inst,
|
|||
const Corner *corner)
|
||||
{
|
||||
cmdLinkedNetwork();
|
||||
PowerResult power = Sta::sta()->power(inst, corner);
|
||||
Sta *sta = Sta::sta();
|
||||
PowerResult power = sta->power(inst, corner);
|
||||
FloatSeq powers;
|
||||
powers.push_back(power.internal());
|
||||
powers.push_back(power.switching());
|
||||
|
|
@ -73,14 +73,16 @@ void
|
|||
set_power_global_activity(float activity,
|
||||
float duty)
|
||||
{
|
||||
Sta::sta()->power()->setGlobalActivity(activity, duty);
|
||||
Power *power = Sta::sta()->power();
|
||||
power->setGlobalActivity(activity, duty);
|
||||
}
|
||||
|
||||
void
|
||||
set_power_input_activity(float activity,
|
||||
float duty)
|
||||
{
|
||||
return Sta::sta()->power()->setInputActivity(activity, duty);
|
||||
Power *power = Sta::sta()->power();
|
||||
return power->setInputActivity(activity, duty);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -88,7 +90,8 @@ set_power_input_port_activity(const Port *input_port,
|
|||
float activity,
|
||||
float duty)
|
||||
{
|
||||
return Sta::sta()->power()->setInputPortActivity(input_port, activity, duty);
|
||||
Power *power = Sta::sta()->power();
|
||||
return power->setInputPortActivity(input_port, activity, duty);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -96,21 +99,24 @@ set_power_pin_activity(const Pin *pin,
|
|||
float activity,
|
||||
float duty)
|
||||
{
|
||||
return Sta::sta()->power()->setUserActivity(pin, activity, duty,
|
||||
PwrActivityOrigin::user);
|
||||
Power *power = Sta::sta()->power();
|
||||
return power->setUserActivity(pin, activity, duty, PwrActivityOrigin::user);
|
||||
}
|
||||
|
||||
void
|
||||
read_vcd_activities(const char *filename,
|
||||
const char *scope)
|
||||
{
|
||||
readVcdActivities(filename, scope, Sta::sta());
|
||||
Sta *sta = Sta::sta();
|
||||
cmdLinkedNetwork();
|
||||
readVcdActivities(filename, scope, sta);
|
||||
}
|
||||
|
||||
void
|
||||
report_vcd_waveforms(const char *filename)
|
||||
{
|
||||
reportVcdWaveforms(filename, Sta::sta());
|
||||
Sta *sta = Sta::sta();
|
||||
reportVcdWaveforms(filename, sta);
|
||||
}
|
||||
|
||||
// debugging
|
||||
|
|
@ -118,7 +124,8 @@ void
|
|||
report_vcd_var_values(const char *filename,
|
||||
const char *var_name)
|
||||
{
|
||||
reportVcdVarValues(filename, var_name, Sta::sta());
|
||||
Sta *sta = Sta::sta();
|
||||
reportVcdVarValues(filename, var_name, sta);
|
||||
}
|
||||
|
||||
%} // inline
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ proc_redirect report_power {
|
|||
keys {-instances -corner -digits} flags {}
|
||||
|
||||
check_argc_eq0 "report_power" $args
|
||||
|
||||
if { ![liberty_libraries_exist] } {
|
||||
sta_error 304 "No liberty libraries have been read."
|
||||
}
|
||||
if { [info exists keys(-digits)] } {
|
||||
set digits $keys(-digits)
|
||||
check_positive_integer "-digits" $digits
|
||||
|
|
@ -51,6 +55,16 @@ proc_redirect report_power {
|
|||
}
|
||||
}
|
||||
|
||||
proc liberty_libraries_exist {} {
|
||||
set lib_iter [liberty_library_iterator]
|
||||
set have_liberty 0
|
||||
if { [$lib_iter has_next] } {
|
||||
set have_liberty 1
|
||||
}
|
||||
$lib_iter finish
|
||||
return $have_liberty
|
||||
}
|
||||
|
||||
proc report_power_design { corner digits } {
|
||||
set power_result [design_power $corner]
|
||||
set totals [lrange $power_result 0 3]
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ ReadVcdActivities::readActivities()
|
|||
setActivities();
|
||||
else
|
||||
report_->warn(1450, "VCD max time is zero.");
|
||||
report_->reportLine("Annotated %lu pin activities.", annotated_pins_.size());
|
||||
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ DeratingFactors::setFactor(PathClkOrData clk_data,
|
|||
const EarlyLate *early_late,
|
||||
float factor)
|
||||
{
|
||||
for (auto tr1 : rf->range())
|
||||
factors_[int(clk_data)].setValue(tr1, early_late, factor);
|
||||
for (auto rf1 : rf->range())
|
||||
factors_[int(clk_data)].setValue(rf1, early_late, factor);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ ExceptionPath::ExceptionPath(ExceptionFrom *from,
|
|||
to_(to),
|
||||
min_max_(min_max),
|
||||
own_pts_(own_pts),
|
||||
priority_(priority)
|
||||
priority_(priority),
|
||||
id_(0)
|
||||
{
|
||||
makeStates();
|
||||
}
|
||||
|
|
@ -125,6 +126,12 @@ ExceptionPath::asString(const Network *network) const
|
|||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
ExceptionPath::setId(size_t id)
|
||||
{
|
||||
id_ = id;
|
||||
}
|
||||
|
||||
ExceptionPt *
|
||||
ExceptionPath::firstPt()
|
||||
{
|
||||
|
|
@ -2247,19 +2254,14 @@ ExceptionState::hash() const
|
|||
return hashSum(exception_->hash(), index_);
|
||||
}
|
||||
|
||||
ExceptionStateLess::ExceptionStateLess(const Network *network) :
|
||||
network_(network)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ExceptionStateLess::operator()(const ExceptionState *state1,
|
||||
const ExceptionState *state2) const
|
||||
{
|
||||
const ExceptionPath *except1 = state1->exception();
|
||||
const ExceptionPath *except2 = state2->exception();
|
||||
ExceptionPathLess except_less(network_);
|
||||
return except_less(except1, except2)
|
||||
return except1->id() < except2->id()
|
||||
//return except1 < except2
|
||||
|| (except1 == except2
|
||||
&& state1->index() < state2->index());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,17 +20,17 @@ namespace sta {
|
|||
|
||||
InputDrive::InputDrive()
|
||||
{
|
||||
for (auto tr_index : RiseFall::rangeIndex()) {
|
||||
for (auto rf_index : RiseFall::rangeIndex()) {
|
||||
for (auto mm_index : MinMax::rangeIndex())
|
||||
drive_cells_[tr_index][mm_index] = nullptr;
|
||||
drive_cells_[rf_index][mm_index] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
InputDrive::~InputDrive()
|
||||
{
|
||||
for (auto tr_index : RiseFall::rangeIndex()) {
|
||||
for (auto rf_index : RiseFall::rangeIndex()) {
|
||||
for (auto mm_index : MinMax::rangeIndex()) {
|
||||
InputDriveCell *drive_cell = drive_cells_[tr_index][mm_index];
|
||||
InputDriveCell *drive_cell = drive_cells_[rf_index][mm_index];
|
||||
delete drive_cell;
|
||||
}
|
||||
}
|
||||
|
|
@ -210,8 +210,8 @@ InputDriveCell::setToPort(const LibertyPort *to_port)
|
|||
void
|
||||
InputDriveCell::setFromSlews(float *from_slews)
|
||||
{
|
||||
for (auto tr_index : RiseFall::rangeIndex())
|
||||
from_slews_[tr_index] = from_slews[tr_index];
|
||||
for (auto rf_index : RiseFall::rangeIndex())
|
||||
from_slews_[rf_index] = from_slews[rf_index];
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
94
sdc/Sdc.cc
94
sdc/Sdc.cc
|
|
@ -110,6 +110,7 @@ Sdc::Sdc(StaState *sta) :
|
|||
disabled_wire_edges_(network_),
|
||||
disabled_clk_gating_checks_inst_(network_),
|
||||
disabled_clk_gating_checks_pin_(network_),
|
||||
exception_id_(0),
|
||||
have_thru_hpin_exceptions_(false),
|
||||
first_thru_edge_exceptions_(0, PinPairHash(network_), PinPairEqual()),
|
||||
path_delay_internal_startpoints_(network_),
|
||||
|
|
@ -271,15 +272,12 @@ Sdc::deleteConstraints()
|
|||
inst_min_pulse_width_map_.deleteContentsClear();
|
||||
clk_min_pulse_width_map_.deleteContentsClear();
|
||||
|
||||
for (auto pin_data_check : data_checks_from_map_) {
|
||||
DataCheckSet *checks = pin_data_check.second;
|
||||
for (auto [pin, checks] : data_checks_from_map_) {
|
||||
checks->deleteContents();
|
||||
delete checks;
|
||||
}
|
||||
for (auto pin_data_check : data_checks_to_map_) {
|
||||
DataCheckSet *checks = pin_data_check.second;
|
||||
for (auto [pin, checks] : data_checks_to_map_)
|
||||
delete checks;
|
||||
}
|
||||
|
||||
input_delays_.deleteContents();
|
||||
input_delay_pin_map_.deleteContents();
|
||||
|
|
@ -323,9 +321,7 @@ Sdc::removeNetLoadCaps()
|
|||
void
|
||||
Sdc::removeLibertyAnnotations()
|
||||
{
|
||||
for (auto cell_port : disabled_cell_ports_) {
|
||||
DisabledCellPorts *disable = cell_port.second;
|
||||
LibertyCell *cell = disable->cell();
|
||||
for (auto [cell, disable] : disabled_cell_ports_) {
|
||||
if (disable->all())
|
||||
cell->setIsDisabledConstraint(false);
|
||||
|
||||
|
|
@ -444,6 +440,57 @@ Sdc::isConstrained(const Net *net) const
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
PortSeq
|
||||
Sdc::allInputs(bool no_clks)
|
||||
{
|
||||
PortSeq ports;
|
||||
Instance *top_inst = network_->topInstance();
|
||||
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);
|
||||
while (pin_iter->hasNext()) {
|
||||
const Pin *pin = pin_iter->next();
|
||||
const Port *port = network_->port(pin);
|
||||
PortDirection *dir = network_->direction(port);
|
||||
if (dir->isAnyInput()
|
||||
&& !(no_clks && isClock(pin)))
|
||||
portMembers(port, ports);
|
||||
}
|
||||
delete pin_iter;
|
||||
return ports;
|
||||
}
|
||||
|
||||
PortSeq
|
||||
Sdc::allOutputs()
|
||||
{
|
||||
PortSeq ports;
|
||||
Instance *top_inst = network_->topInstance();
|
||||
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);
|
||||
while (pin_iter->hasNext()) {
|
||||
const Pin *pin = pin_iter->next();
|
||||
const Port *port = network_->port(pin);
|
||||
PortDirection *dir = network_->direction(port);
|
||||
if (dir->isAnyOutput())
|
||||
portMembers(port, ports);
|
||||
}
|
||||
delete pin_iter;
|
||||
return ports;
|
||||
}
|
||||
|
||||
void
|
||||
Sdc::portMembers(const Port *port,
|
||||
PortSeq &ports)
|
||||
{
|
||||
if (network_->isBus(port)) {
|
||||
PortMemberIterator *member_iter = network_->memberIterator(port);
|
||||
while (member_iter->hasNext()) {
|
||||
Port *member = member_iter->next();
|
||||
ports.push_back(member);
|
||||
}
|
||||
delete member_iter;
|
||||
}
|
||||
else
|
||||
ports.push_back(port);
|
||||
}
|
||||
|
||||
void
|
||||
Sdc::setAnalysisType(AnalysisType analysis_type)
|
||||
{
|
||||
|
|
@ -1906,8 +1953,8 @@ void
|
|||
Sdc::ensureClkGroupExclusions()
|
||||
{
|
||||
if (clk_group_exclusions_.empty()) {
|
||||
for (auto name_clk_groups : clk_groups_name_map_)
|
||||
makeClkGroupExclusions(name_clk_groups.second);
|
||||
for (const auto [name, clk_groups] : clk_groups_name_map_)
|
||||
makeClkGroupExclusions(clk_groups);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2020,8 +2067,7 @@ Sdc::removeClockGroupsLogicallyExclusive(const char *name)
|
|||
removeClockGroups(groups);
|
||||
}
|
||||
else {
|
||||
for (auto name_group : clk_groups_name_map_) {
|
||||
ClockGroups *groups = name_group.second;
|
||||
for (const auto [name, groups] : clk_groups_name_map_) {
|
||||
if (groups->logicallyExclusive())
|
||||
removeClockGroups(groups);
|
||||
}
|
||||
|
|
@ -2037,8 +2083,7 @@ Sdc::removeClockGroupsPhysicallyExclusive(const char *name)
|
|||
removeClockGroups(groups);
|
||||
}
|
||||
else {
|
||||
for (auto name_group : clk_groups_name_map_) {
|
||||
ClockGroups *groups = name_group.second;
|
||||
for (const auto [name, groups] : clk_groups_name_map_) {
|
||||
if (groups->physicallyExclusive())
|
||||
removeClockGroups(groups);
|
||||
}
|
||||
|
|
@ -2054,8 +2099,7 @@ Sdc::removeClockGroupsAsynchronous(const char *name)
|
|||
removeClockGroups(groups);
|
||||
}
|
||||
else {
|
||||
for (auto name_group : clk_groups_name_map_) {
|
||||
ClockGroups *groups = name_group.second;
|
||||
for (const auto [name, groups] : clk_groups_name_map_) {
|
||||
if (groups->asynchronous())
|
||||
removeClockGroups(groups);
|
||||
}
|
||||
|
|
@ -2075,10 +2119,8 @@ Sdc::removeClockGroups(ClockGroups *groups)
|
|||
void
|
||||
Sdc::clockGroupsDeleteClkRefs(Clock *clk)
|
||||
{
|
||||
for (auto name_group : clk_groups_name_map_) {
|
||||
ClockGroups *groups = name_group.second;
|
||||
for (const auto [name, groups] : clk_groups_name_map_)
|
||||
groups->removeClock(clk);
|
||||
}
|
||||
clearClkGroupExclusions();
|
||||
}
|
||||
|
||||
|
|
@ -3983,9 +4025,7 @@ Sdc::clearGroupPathMap()
|
|||
{
|
||||
// GroupPath exceptions are deleted with other exceptions.
|
||||
// Delete group_path name strings.
|
||||
for (auto name_groups : group_path_map_) {
|
||||
const char *name = name_groups.first;
|
||||
GroupPathSet *groups = name_groups.second;
|
||||
for (auto [name, groups] : group_path_map_) {
|
||||
stringDelete(name);
|
||||
groups->deleteContents();
|
||||
delete groups;
|
||||
|
|
@ -4513,6 +4553,7 @@ void
|
|||
Sdc::recordException(ExceptionPath *exception)
|
||||
{
|
||||
exceptions_.insert(exception);
|
||||
exception->setId(++exception_id_);
|
||||
recordMergeHashes(exception);
|
||||
recordExceptionFirstPts(exception);
|
||||
checkForThruHpins(exception);
|
||||
|
|
@ -4774,6 +4815,7 @@ void
|
|||
Sdc::deleteExceptions()
|
||||
{
|
||||
exceptions_.deleteContentsClear();
|
||||
exception_id_ = 0;
|
||||
|
||||
first_from_pin_exceptions_.deleteContentsClear();
|
||||
first_from_clk_exceptions_.deleteContentsClear();
|
||||
|
|
@ -5164,7 +5206,7 @@ Sdc::exceptionFromStates(const ExceptionPathSet *exceptions,
|
|||
// but flush all other exception states because they are lower
|
||||
// priority.
|
||||
if (states == nullptr)
|
||||
states = new ExceptionStateSet(network_);
|
||||
states = new ExceptionStateSet();
|
||||
states->clear();
|
||||
states->insert(state);
|
||||
// No need to examine other exceptions from this
|
||||
|
|
@ -5172,7 +5214,7 @@ Sdc::exceptionFromStates(const ExceptionPathSet *exceptions,
|
|||
return false;
|
||||
}
|
||||
if (states == nullptr)
|
||||
states = new ExceptionStateSet(network_);
|
||||
states = new ExceptionStateSet();
|
||||
states->insert(state);
|
||||
}
|
||||
}
|
||||
|
|
@ -5219,7 +5261,7 @@ Sdc::filterRegQStates(const Pin *to_pin,
|
|||
&& exception->matchesFirstPt(to_rf, min_max)) {
|
||||
ExceptionState *state = exception->firstState();
|
||||
if (states == nullptr)
|
||||
states = new ExceptionStateSet(network_);
|
||||
states = new ExceptionStateSet();
|
||||
states->insert(state);
|
||||
}
|
||||
}
|
||||
|
|
@ -5263,7 +5305,7 @@ Sdc::exceptionThruStates(const ExceptionPathSet *exceptions,
|
|||
if (exception->matchesFirstPt(to_rf, min_max)) {
|
||||
ExceptionState *state = exception->firstState();
|
||||
if (states == nullptr)
|
||||
states = new ExceptionStateSet(network_);
|
||||
states = new ExceptionStateSet();
|
||||
states->insert(state);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -296,50 +296,21 @@ proc all_clocks { } {
|
|||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "all_inputs" {}
|
||||
define_cmd_args "all_inputs" {[-no_clocks]}
|
||||
|
||||
proc all_inputs { } {
|
||||
return [all_ports_for_direction "input"]
|
||||
proc all_inputs { args } {
|
||||
parse_key_args "all_inputs" args keys {} flags {-no_clocks}
|
||||
set no_clks [info exists flags(-no_clocks)]
|
||||
return [all_inputs_cmd $no_clks]
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "all_outputs" {}
|
||||
|
||||
proc all_outputs { } {
|
||||
return [all_ports_for_direction "output"]
|
||||
}
|
||||
|
||||
proc all_ports_for_direction { direction } {
|
||||
set top_instance [top_instance]
|
||||
set top_cell [$top_instance cell]
|
||||
set ports {}
|
||||
set iter [$top_cell port_iterator]
|
||||
while {[$iter has_next]} {
|
||||
set port [$iter next]
|
||||
set port_dir [port_direction $port]
|
||||
if { $port_dir == $direction || $port_dir == "bidirect" } {
|
||||
set ports [concat $ports [port_members $port]]
|
||||
}
|
||||
}
|
||||
$iter finish
|
||||
return $ports
|
||||
}
|
||||
|
||||
proc port_members { port } {
|
||||
if [$port is_bus] {
|
||||
# Expand bus ports.
|
||||
set ports {}
|
||||
set member_iter [$port member_iterator]
|
||||
while {[$member_iter has_next]} {
|
||||
set bit_port [$member_iter next]
|
||||
lappend ports $bit_port
|
||||
}
|
||||
$member_iter finish
|
||||
return $ports
|
||||
} else {
|
||||
return $port
|
||||
}
|
||||
proc all_outputs { args } {
|
||||
check_argc_eq0 "all_outputs" $args
|
||||
return [all_outputs_cmd]
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
|
@ -1329,7 +1300,7 @@ proc set_clock_gating_check { args } {
|
|||
flags {-rise -fall -high -low}
|
||||
|
||||
check_argc_eq0or1 "set_clock_gating_check" $args
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
|
||||
set active_value ""
|
||||
if {[info exists flags(-high)] && [info exists flags(-low)]} {
|
||||
|
|
@ -1344,20 +1315,20 @@ proc set_clock_gating_check { args } {
|
|||
sta_error 397 "missing -setup or -hold argument."
|
||||
}
|
||||
if [info exists keys(-hold)] {
|
||||
set_clock_gating_check1 $args $tr "min" $keys(-hold) $active_value
|
||||
set_clock_gating_check1 $args $rf "min" $keys(-hold) $active_value
|
||||
}
|
||||
if [info exists keys(-setup)] {
|
||||
set_clock_gating_check1 $args $tr "max" $keys(-setup) $active_value
|
||||
set_clock_gating_check1 $args $rf "max" $keys(-setup) $active_value
|
||||
}
|
||||
}
|
||||
|
||||
proc set_clock_gating_check1 { args tr setup_hold margin active_value } {
|
||||
proc set_clock_gating_check1 { args rf setup_hold margin active_value } {
|
||||
set margin [time_ui_sta $margin]
|
||||
if { [llength $args] == 0 } {
|
||||
if { $active_value != "" } {
|
||||
sta_error 398 "-high and -low only permitted for pins and instances."
|
||||
}
|
||||
set_clock_gating_check_cmd $tr $setup_hold $margin
|
||||
set_clock_gating_check_cmd $rf $setup_hold $margin
|
||||
} elseif { [llength $args] == 1 } {
|
||||
parse_clk_inst_port_pin_arg [lindex $args 0] clks insts pins
|
||||
|
||||
|
|
@ -1365,18 +1336,18 @@ proc set_clock_gating_check1 { args tr setup_hold margin active_value } {
|
|||
sta_error 399 "-high and -low only permitted for pins and instances."
|
||||
}
|
||||
foreach clk $clks {
|
||||
set_clock_gating_check_clk_cmd $clk $tr $setup_hold $margin
|
||||
set_clock_gating_check_clk_cmd $clk $rf $setup_hold $margin
|
||||
}
|
||||
|
||||
if { $active_value == "" } {
|
||||
set active_value "X"
|
||||
}
|
||||
foreach pin $pins {
|
||||
set_clock_gating_check_pin_cmd $pin $tr $setup_hold \
|
||||
set_clock_gating_check_pin_cmd $pin $rf $setup_hold \
|
||||
$margin $active_value
|
||||
}
|
||||
foreach inst $insts {
|
||||
set_clock_gating_check_instance_cmd $inst $tr $setup_hold \
|
||||
set_clock_gating_check_instance_cmd $inst $rf $setup_hold \
|
||||
$margin $active_value
|
||||
}
|
||||
}
|
||||
|
|
@ -1515,7 +1486,7 @@ proc set_clock_latency { args } {
|
|||
|
||||
parse_clk_port_pin_arg $objects clks pins
|
||||
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
set min_max [parse_min_max_all_flags flags]
|
||||
|
||||
set pin_clk "NULL"
|
||||
|
|
@ -1531,14 +1502,14 @@ proc set_clock_latency { args } {
|
|||
set early_late [parse_early_late_all_flags flags]
|
||||
|
||||
foreach clk $clks {
|
||||
set_clock_insertion_cmd $clk "NULL" $tr $min_max $early_late $delay
|
||||
set_clock_insertion_cmd $clk "NULL" $rf $min_max $early_late $delay
|
||||
}
|
||||
foreach pin $pins {
|
||||
# Source only allowed on clocks and clock pins.
|
||||
if { ![is_clock_src $pin] } {
|
||||
sta_error 409 "-source '[get_full_name $pin]' is not a clock pin."
|
||||
}
|
||||
set_clock_insertion_cmd $pin_clk $pin $tr $min_max $early_late $delay
|
||||
set_clock_insertion_cmd $pin_clk $pin $rf $min_max $early_late $delay
|
||||
}
|
||||
} else {
|
||||
# Latency.
|
||||
|
|
@ -1547,10 +1518,10 @@ proc set_clock_latency { args } {
|
|||
}
|
||||
|
||||
foreach clk $clks {
|
||||
set_clock_latency_cmd $clk "NULL" $tr $min_max $delay
|
||||
set_clock_latency_cmd $clk "NULL" $rf $min_max $delay
|
||||
}
|
||||
foreach pin $pins {
|
||||
set_clock_latency_cmd $pin_clk $pin $tr $min_max $delay
|
||||
set_clock_latency_cmd $pin_clk $pin $rf $min_max $delay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1676,7 +1647,7 @@ proc set_clock_transition { args } {
|
|||
parse_key_args "set_clock_transition" args keys {} \
|
||||
flags {-rise -fall -max -min}
|
||||
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
set min_max [parse_min_max_all_flags flags]
|
||||
check_argc_eq2 "set_clock_transition" $args
|
||||
|
||||
|
|
@ -1687,7 +1658,7 @@ proc set_clock_transition { args } {
|
|||
if { [$clk is_virtual] } {
|
||||
sta_warn 419 "transition time can not be specified for virtual clocks."
|
||||
} else {
|
||||
set_clock_slew_cmd $clk $tr $min_max [time_ui_sta $slew]
|
||||
set_clock_slew_cmd $clk $rf $min_max [time_ui_sta $slew]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2098,6 +2069,22 @@ proc parse_disable_inst_ports { inst port_name } {
|
|||
return $ports
|
||||
}
|
||||
|
||||
proc port_members { port } {
|
||||
if [$port is_bus] {
|
||||
# Expand bus ports.
|
||||
set ports {}
|
||||
set member_iter [$port member_iterator]
|
||||
while {[$member_iter has_next]} {
|
||||
set bit_port [$member_iter next]
|
||||
lappend ports $bit_port
|
||||
}
|
||||
$member_iter finish
|
||||
return $ports
|
||||
} else {
|
||||
return $port
|
||||
}
|
||||
}
|
||||
|
||||
proc set_disable_timing_cell { cell from to } {
|
||||
set from_ports [parse_disable_cell_ports $cell $from]
|
||||
set to_ports [parse_disable_cell_ports $cell $to]
|
||||
|
|
@ -2114,7 +2101,7 @@ proc set_disable_timing_cell { cell from to } {
|
|||
} else {
|
||||
foreach from_port $from_ports {
|
||||
foreach to_port $to_ports {
|
||||
disable_cell $cell $from_port $to_port
|
||||
disable_cell $cell $from_port $to_port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2150,7 +2137,7 @@ proc unset_disable_timing { args } {
|
|||
proc unset_disable_cmd { cmd cmd_args } {
|
||||
parse_key_args $cmd cmd_args keys {-from -to} flags {}
|
||||
check_argc_eq1 $cmd $cmd_args
|
||||
|
||||
|
||||
set from ""
|
||||
if { [info exists keys(-from)] } {
|
||||
set from $keys(-from)
|
||||
|
|
@ -2161,12 +2148,12 @@ proc unset_disable_cmd { cmd cmd_args } {
|
|||
}
|
||||
parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg $cmd_args \
|
||||
libcells libports insts ports pins edges timing_arc_sets
|
||||
|
||||
|
||||
if { ([info exists keys(-from)] || [info exists keys(-to)]) \
|
||||
&& ($libports != {} || $pins != {} || $ports != {}) } {
|
||||
&& ($libports != {} || $pins != {} || $ports != {}) } {
|
||||
sta_warn 434 "-from/-to keywords ignored for lib_pin, port and pin arguments."
|
||||
}
|
||||
|
||||
|
||||
foreach libcell $libcells {
|
||||
unset_disable_timing_cell $libcell $from $to
|
||||
}
|
||||
|
|
@ -2206,7 +2193,7 @@ proc unset_disable_timing_cell { cell from to } {
|
|||
} else {
|
||||
foreach from_port $from_ports {
|
||||
foreach to_port $to_ports {
|
||||
unset_disable_cell $cell $from_port $to_port
|
||||
unset_disable_cell $cell $from_port $to_port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2231,7 +2218,7 @@ proc unset_disable_timing_instance { inst from to } {
|
|||
} else {
|
||||
foreach from_port $from_ports {
|
||||
foreach to_port $to_ports {
|
||||
unset_disable_instance $inst $from_port $to_port
|
||||
unset_disable_instance $inst $from_port $to_port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2277,7 +2264,7 @@ proc set_false_path { args } {
|
|||
sta_warn 437 "-from, -through or -to required."
|
||||
} else {
|
||||
if [info exists flags(-reset_path)] {
|
||||
reset_path_cmd $from $thrus $to $min_max
|
||||
reset_path_cmd $from $thrus $to $min_max
|
||||
}
|
||||
|
||||
set comment [parse_comment_key keys]
|
||||
|
|
@ -2336,7 +2323,7 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } {
|
|||
parse_key_args $cmd cmd_args \
|
||||
keys {-clock -reference_pin} \
|
||||
flags {-rise -fall -max -min -clock_fall -add_delay \
|
||||
-source_latency_included -network_latency_included}
|
||||
-source_latency_included -network_latency_included}
|
||||
check_argc_eq2 $cmd $cmd_args
|
||||
|
||||
set delay_arg [lindex $cmd_args 0]
|
||||
|
|
@ -2366,7 +2353,7 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } {
|
|||
set clk_rf "rise"
|
||||
}
|
||||
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
set min_max [parse_min_max_all_flags flags]
|
||||
set add [info exists flags(-add_delay)]
|
||||
set source_latency_included [info exists flags(-source_latency_included)]
|
||||
|
|
@ -2374,14 +2361,14 @@ proc set_port_delay { cmd sta_cmd cmd_args port_dirs } {
|
|||
|
||||
foreach pin $pins {
|
||||
if { [$pin is_top_level_port] \
|
||||
&& [lsearch $port_dirs [pin_direction $pin]] == -1 } {
|
||||
&& [lsearch $port_dirs [pin_direction $pin]] == -1 } {
|
||||
sta_warn 440 "$cmd not allowed on [pin_direction $pin] port '[get_full_name $pin]'."
|
||||
} elseif { $clk != "NULL" && [lsearch [$clk sources] $pin] != -1 } {
|
||||
sta_warn 441 "$cmd relative to a clock defined on the same port/pin not allowed."
|
||||
} else {
|
||||
$sta_cmd $pin $tr $clk $clk_rf $ref_pin\
|
||||
$source_latency_included $network_latency_included \
|
||||
$min_max $add $delay
|
||||
$sta_cmd $pin $rf $clk $clk_rf $ref_pin\
|
||||
$source_latency_included $network_latency_included \
|
||||
$min_max $add $delay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2603,7 +2590,7 @@ proc unset_path_exceptions_cmd { cmd cmd_args } {
|
|||
parse_key_args $cmd cmd_args \
|
||||
keys {-from -rise_from -fall_from -to -rise_to -fall_to} \
|
||||
flags {-setup -hold -rise -fall} 0
|
||||
|
||||
|
||||
set min_max "min_max"
|
||||
if { [info exists flags(-setup)] && ![info exists flags(-hold)] } {
|
||||
set min_max "max"
|
||||
|
|
@ -2611,7 +2598,7 @@ proc unset_path_exceptions_cmd { cmd cmd_args } {
|
|||
if { [info exists flags(-hold)] && ![info exists flags(-setup)] } {
|
||||
set min_max "min"
|
||||
}
|
||||
|
||||
|
||||
set arg_error 0
|
||||
set from [parse_from_arg keys arg_error]
|
||||
set thrus [parse_thrus_arg cmd_args arg_error]
|
||||
|
|
@ -2621,7 +2608,7 @@ proc unset_path_exceptions_cmd { cmd cmd_args } {
|
|||
sta_error 447 "$cmd command failed."
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
check_for_key_args $cmd cmd_args
|
||||
if { $cmd_args != {} } {
|
||||
delete_from_thrus_to $from $thrus $to
|
||||
|
|
@ -2631,7 +2618,7 @@ proc unset_path_exceptions_cmd { cmd cmd_args } {
|
|||
delete_from_thrus_to $from $thrus $to
|
||||
sta_error 449 "-from, -through or -to required."
|
||||
}
|
||||
|
||||
|
||||
reset_path_cmd $from $thrus $to $min_max
|
||||
delete_from_thrus_to $from $thrus $to
|
||||
}
|
||||
|
|
@ -2680,11 +2667,11 @@ proc unset_port_delay { cmd swig_cmd cmd_args } {
|
|||
set clk_rf "rise"
|
||||
}
|
||||
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
set min_max [parse_min_max_all_flags flags]
|
||||
|
||||
|
||||
foreach pin $pins {
|
||||
$swig_cmd $pin $tr $clk $clk_rf $min_max
|
||||
$swig_cmd $pin $rf $clk $clk_rf $min_max
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2731,13 +2718,13 @@ define_cmd_args "set_case_analysis" \
|
|||
|
||||
proc set_case_analysis { value pins } {
|
||||
if { !($value == "0" \
|
||||
|| $value == "1" \
|
||||
|| $value == "zero" \
|
||||
|| $value == "one" \
|
||||
|| $value == "rise" \
|
||||
|| $value == "rising" \
|
||||
|| $value == "fall" \
|
||||
|| $value == "falling") } {
|
||||
|| $value == "1" \
|
||||
|| $value == "zero" \
|
||||
|| $value == "one" \
|
||||
|| $value == "rise" \
|
||||
|| $value == "rising" \
|
||||
|| $value == "fall" \
|
||||
|| $value == "falling") } {
|
||||
sta_error 451 "value must be 0, zero, 1, one, rise, rising, fall, or falling."
|
||||
}
|
||||
set pins1 [get_port_pins_error "pins" $pins]
|
||||
|
|
@ -2760,11 +2747,11 @@ proc unset_case_analysis { pins } {
|
|||
################################################################
|
||||
|
||||
define_cmd_args "set_drive" {[-rise] [-fall] [-min] [-max] \
|
||||
resistance ports}
|
||||
resistance ports}
|
||||
|
||||
proc set_drive { args } {
|
||||
parse_key_args "set_drive" args keys {} flags {-rise -fall -min -max}
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
set min_max [parse_min_max_all_check_flags flags]
|
||||
|
||||
check_argc_eq2 "set_drive" $args
|
||||
|
|
@ -2774,7 +2761,7 @@ proc set_drive { args } {
|
|||
set res [resistance_ui_sta $res]
|
||||
set ports [get_ports_error "ports" [lindex $args 1]]
|
||||
foreach port $ports {
|
||||
set_drive_resistance_cmd $port $tr $min_max $res
|
||||
set_drive_resistance_cmd $port $rf $min_max $res
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2790,10 +2777,10 @@ define_cmd_args "set_driving_cell" \
|
|||
proc set_driving_cell { args } {
|
||||
parse_key_args "set_driving_cell" args \
|
||||
keys {-lib_cell -cell -library -pin -from_pin -multiply_by \
|
||||
-input_transition_rise -input_transition_fall} \
|
||||
-input_transition_rise -input_transition_fall} \
|
||||
flags {-rise -fall -min -max -dont_scale -no_design_rule}
|
||||
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
set min_max [parse_min_max_all_flags flags]
|
||||
|
||||
# -cell is an undocumented non-sdc alias for -lib_cell.
|
||||
|
|
@ -2807,13 +2794,13 @@ proc set_driving_cell { args } {
|
|||
set library [get_liberty_error "library" $keys(-library)]
|
||||
set cell [$library find_liberty_cell $cell_name]
|
||||
if { $cell == "NULL" } {
|
||||
sta_error 452 "cell '$lib_name:$cell_name' not found."
|
||||
sta_error 452 "cell '$lib_name:$cell_name' not found."
|
||||
}
|
||||
} else {
|
||||
set library "NULL"
|
||||
set cell [find_liberty_cell $cell_name]
|
||||
if { $cell == "NULL" } {
|
||||
sta_error 453 "'$cell_name' not found."
|
||||
sta_error 453 "'$cell_name' not found."
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -2834,14 +2821,14 @@ proc set_driving_cell { args } {
|
|||
set port [$port_iter next]
|
||||
set dir [liberty_port_direction $port]
|
||||
if { [port_direction_any_output $dir] } {
|
||||
incr output_count
|
||||
if { $output_count > 1 } {
|
||||
$port_iter finish
|
||||
sta_error 456 "-pin argument required for cells with multiple outputs."
|
||||
}
|
||||
set to_port $port
|
||||
# No break. Keep looking for output ports to make sure there
|
||||
# is only one.
|
||||
incr output_count
|
||||
if { $output_count > 1 } {
|
||||
$port_iter finish
|
||||
sta_error 456 "-pin argument required for cells with multiple outputs."
|
||||
}
|
||||
set to_port $port
|
||||
# No break. Keep looking for output ports to make sure there
|
||||
# is only one.
|
||||
}
|
||||
}
|
||||
$port_iter finish
|
||||
|
|
@ -2884,14 +2871,14 @@ proc set_driving_cell { args } {
|
|||
set ports [get_ports_error "ports" [lindex $args 0]]
|
||||
foreach port $ports {
|
||||
set_drive_cell_cmd $library $cell $port $from_port \
|
||||
$from_slew_rise $from_slew_fall $to_port $tr $min_max
|
||||
$from_slew_rise $from_slew_fall $to_port $rf $min_max
|
||||
}
|
||||
}
|
||||
|
||||
proc port_direction_any_output { dir } {
|
||||
return [expr { $dir == "output" \
|
||||
|| $dir == "bidirect" \
|
||||
|| $dir == "tristate" } ]
|
||||
|| $dir == "bidirect" \
|
||||
|| $dir == "tristate" } ]
|
||||
}
|
||||
|
||||
################################################################
|
||||
|
|
@ -2911,7 +2898,7 @@ proc set_input_transition { args } {
|
|||
parse_key_args "set_input_transition" args keys {-clock} \
|
||||
flags {-rise -fall -max -min -clock_fall}
|
||||
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
set min_max [parse_min_max_all_flags flags]
|
||||
|
||||
|
||||
|
|
@ -2930,7 +2917,7 @@ proc set_input_transition { args } {
|
|||
}
|
||||
|
||||
foreach port $ports {
|
||||
set_input_slew_cmd $port $tr $min_max $slew
|
||||
set_input_slew_cmd $port $rf $min_max $slew
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2966,11 +2953,11 @@ proc set_load { args } {
|
|||
# -pin_load is the default.
|
||||
if { $pin_load || (!$pin_load && !$wire_load) } {
|
||||
foreach port $ports {
|
||||
set_port_ext_pin_cap $port $rf $corner $min_max $cap
|
||||
set_port_ext_pin_cap $port $rf $corner $min_max $cap
|
||||
}
|
||||
} elseif { $wire_load } {
|
||||
foreach port $ports {
|
||||
set_port_ext_wire_cap $port $subtract_pin_load $rf $corner $min_max $cap
|
||||
set_port_ext_wire_cap $port $subtract_pin_load $rf $corner $min_max $cap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3095,11 +3082,11 @@ proc set_max_transition { args } {
|
|||
set objects [lindex $args 1]
|
||||
parse_clk_cell_port_args $objects clks cells ports
|
||||
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
|
||||
set path_types {}
|
||||
if { ![info exists flags(-clock_path)] \
|
||||
&& ![info exists flags(-data_path)] } {
|
||||
&& ![info exists flags(-data_path)] } {
|
||||
# Derate clk and data if neither -clock_path or -data_path.
|
||||
set path_types {"clk" "data"}
|
||||
}
|
||||
|
|
@ -3111,17 +3098,17 @@ proc set_max_transition { args } {
|
|||
}
|
||||
|
||||
if { ($ports != {} || $cells != {}) \
|
||||
&& ([info exists flags(-clock_path)] \
|
||||
|| [info exists flags(-data_path)]
|
||||
|| [info exists flags(-rise)]
|
||||
|| [info exists flags(-fall)]) } {
|
||||
&& ([info exists flags(-clock_path)] \
|
||||
|| [info exists flags(-data_path)]
|
||||
|| [info exists flags(-rise)]
|
||||
|| [info exists flags(-fall)]) } {
|
||||
sta_warn 468 "-data_path, -clock_path, -rise, -fall ignored for ports and designs."
|
||||
}
|
||||
|
||||
# -clock_path/-data_path and transition only apply to clock objects.
|
||||
foreach path_type $path_types {
|
||||
foreach clk $clks {
|
||||
set_slew_limit_clk $clk $tr $path_type "max" $slew
|
||||
set_slew_limit_clk $clk $rf $path_type "max" $slew
|
||||
}
|
||||
}
|
||||
foreach cell $cells {
|
||||
|
|
@ -3180,7 +3167,7 @@ define_cmd_args "set_timing_derate" \
|
|||
proc set_timing_derate { args } {
|
||||
parse_key_args "set_timing_derate" args keys {} \
|
||||
flags {-rise -fall -early -late -clock -data \
|
||||
-net_delay -cell_delay -cell_check}
|
||||
-net_delay -cell_delay -cell_check}
|
||||
check_argc_eq1or2 "set_timing_derate" $args
|
||||
|
||||
set derate [lindex $args 0]
|
||||
|
|
@ -3189,12 +3176,12 @@ proc set_timing_derate { args } {
|
|||
sta_warn 469 "derating factor greater than 2.0."
|
||||
}
|
||||
|
||||
set tr [parse_rise_fall_flags flags]
|
||||
set rf [parse_rise_fall_flags flags]
|
||||
set early_late [parse_early_late_flags flags]
|
||||
|
||||
set path_types {}
|
||||
if { ![info exists flags(-clock)] \
|
||||
&& ![info exists flags(-data)] } {
|
||||
&& ![info exists flags(-data)] } {
|
||||
# Derate clk and data if neither -clock or -data.
|
||||
lappend path_types "clk"
|
||||
lappend path_types "data"
|
||||
|
|
@ -3222,42 +3209,42 @@ proc set_timing_derate { args } {
|
|||
parse_libcell_inst_net_arg $objects libcells insts nets
|
||||
if { $nets != {} } {
|
||||
if { [info exists flags(-cell_delay)] \
|
||||
|| [info exists flags(-cell_check)] } {
|
||||
sta_warn 470 "-cell_delay and -cell_check flags ignored for net objects."
|
||||
|| [info exists flags(-cell_check)] } {
|
||||
sta_warn 470 "-cell_delay and -cell_check flags ignored for net objects."
|
||||
}
|
||||
foreach net $nets {
|
||||
foreach path_type $path_types {
|
||||
set_timing_derate_net_cmd $net $path_type $tr $early_late $derate
|
||||
}
|
||||
foreach path_type $path_types {
|
||||
set_timing_derate_net_cmd $net $path_type $rf $early_late $derate
|
||||
}
|
||||
}
|
||||
}
|
||||
if { ![info exists flags(-cell_delay)] \
|
||||
&& ![info exists flags(-cell_check)] } {
|
||||
&& ![info exists flags(-cell_check)] } {
|
||||
# Cell checks are not derated if no flags are specified.
|
||||
set derate_types {cell_delay}
|
||||
}
|
||||
foreach derate_type $derate_types {
|
||||
foreach path_type $path_types {
|
||||
foreach inst $insts {
|
||||
set_timing_derate_inst_cmd $inst $derate_type $path_type \
|
||||
$tr $early_late $derate
|
||||
}
|
||||
foreach libcell $libcells {
|
||||
set_timing_derate_cell_cmd $libcell $derate_type $path_type \
|
||||
$tr $early_late $derate
|
||||
}
|
||||
foreach inst $insts {
|
||||
set_timing_derate_inst_cmd $inst $derate_type $path_type \
|
||||
$rf $early_late $derate
|
||||
}
|
||||
foreach libcell $libcells {
|
||||
set_timing_derate_cell_cmd $libcell $derate_type $path_type \
|
||||
$rf $early_late $derate
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if { ![info exists flags(-net_delay)] \
|
||||
&& ![info exists flags(-cell_delay)] \
|
||||
&& ![info exists flags(-cell_check)] } {
|
||||
&& ![info exists flags(-cell_delay)] \
|
||||
&& ![info exists flags(-cell_check)] } {
|
||||
# Cell checks are not derated if no flags are specified.
|
||||
set derate_types {net_delay cell_delay}
|
||||
}
|
||||
foreach derate_type $derate_types {
|
||||
foreach path_type $path_types {
|
||||
set_timing_derate_cmd $derate_type $path_type $tr $early_late $derate
|
||||
set_timing_derate_cmd $derate_type $path_type $rf $early_late $derate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3279,13 +3266,13 @@ proc parse_from_arg { keys_var arg_error_var } {
|
|||
|
||||
if [info exists keys(-from)] {
|
||||
set key "-from"
|
||||
set tr "rise_fall"
|
||||
set rf "rise_fall"
|
||||
} elseif [info exists keys(-rise_from)] {
|
||||
set key "-rise_from"
|
||||
set tr "rise"
|
||||
set rf "rise"
|
||||
} elseif [info exists keys(-fall_from)] {
|
||||
set key "-fall_from"
|
||||
set tr "fall"
|
||||
set rf "fall"
|
||||
} else {
|
||||
return "NULL"
|
||||
}
|
||||
|
|
@ -3296,7 +3283,7 @@ proc parse_from_arg { keys_var arg_error_var } {
|
|||
sta_warn 471 "no valid objects specified for $key."
|
||||
return "NULL"
|
||||
}
|
||||
return [make_exception_from $from_pins $from_clks $from_insts $tr]
|
||||
return [make_exception_from $from_pins $from_clks $from_insts $rf]
|
||||
}
|
||||
|
||||
# "arg_error" is set to notify the caller to cleanup and post error.
|
||||
|
|
@ -3307,29 +3294,29 @@ proc parse_thrus_arg { args_var arg_error_var } {
|
|||
set args_rtn {}
|
||||
while { $args != {} } {
|
||||
set arg [lindex $args 0]
|
||||
set tr ""
|
||||
set rf ""
|
||||
if { $arg == "-through" } {
|
||||
set tr "rise_fall"
|
||||
set rf "rise_fall"
|
||||
set key "-through"
|
||||
} elseif { $arg == "-rise_through" } {
|
||||
set tr "rise"
|
||||
set rf "rise"
|
||||
set key "-rise_through"
|
||||
} elseif { $arg == "-fall_through" } {
|
||||
set tr "fall"
|
||||
set rf "fall"
|
||||
set key "-fall_through"
|
||||
}
|
||||
if { $tr != "" } {
|
||||
if { $rf != "" } {
|
||||
if { [llength $args] > 1 } {
|
||||
set args [lrange $args 1 end]
|
||||
set arg [lindex $args 0]
|
||||
parse_inst_port_pin_net_arg $arg insts pins nets
|
||||
if {$pins == {} && $insts == {} && $nets == {}} {
|
||||
upvar 1 $arg_error_var arg_error
|
||||
set arg_error 1
|
||||
sta_warn 472 "no valid objects specified for $key"
|
||||
} else {
|
||||
lappend thrus [make_exception_thru $pins $nets $insts $tr]
|
||||
}
|
||||
set args [lrange $args 1 end]
|
||||
set arg [lindex $args 0]
|
||||
parse_inst_port_pin_net_arg $arg insts pins nets
|
||||
if {$pins == {} && $insts == {} && $nets == {}} {
|
||||
upvar 1 $arg_error_var arg_error
|
||||
set arg_error 1
|
||||
sta_warn 472 "no valid objects specified for $key"
|
||||
} else {
|
||||
lappend thrus [make_exception_thru $pins $nets $insts $rf]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lappend args_rtn $arg
|
||||
|
|
@ -3454,9 +3441,9 @@ proc parse_op_cond { op_cond_name lib_key min_max key_var } {
|
|||
set lib [$lib_iter next]
|
||||
set op_cond [$lib find_operating_conditions $op_cond_name]
|
||||
if { $op_cond != "NULL" } {
|
||||
set_operating_conditions_cmd $op_cond $min_max
|
||||
set found 1
|
||||
break
|
||||
set_operating_conditions_cmd $op_cond $min_max
|
||||
set found 1
|
||||
break
|
||||
}
|
||||
}
|
||||
$lib_iter finish
|
||||
|
|
@ -3471,8 +3458,8 @@ proc parse_op_cond_analysis_type { key_var } {
|
|||
if [info exists keys(-analysis_type)] {
|
||||
set analysis_type $keys(-analysis_type)
|
||||
if { $analysis_type == "single" \
|
||||
|| $analysis_type == "bc_wc" \
|
||||
|| $analysis_type == "on_chip_variation" } {
|
||||
|| $analysis_type == "bc_wc" \
|
||||
|| $analysis_type == "on_chip_variation" } {
|
||||
set_analysis_type_cmd $analysis_type
|
||||
} else {
|
||||
sta_error 476 "-analysis_type must be single, bc_wc or on_chip_variation."
|
||||
|
|
@ -3496,8 +3483,8 @@ define_cmd_args "set_wire_load_mode" "top|enclosed|segmented"
|
|||
|
||||
proc set_wire_load_mode { mode } {
|
||||
if { $mode == "top" \
|
||||
|| $mode == "enclosed" \
|
||||
|| $mode == "segmented" } {
|
||||
|| $mode == "enclosed" \
|
||||
|| $mode == "segmented" } {
|
||||
set_wire_load_mode_cmd $mode
|
||||
} else {
|
||||
sta_error 478 "mode must be top, enclosed or segmented."
|
||||
|
|
@ -3531,7 +3518,7 @@ proc set_wire_load_model { args } {
|
|||
set lib [$lib_iter next]
|
||||
set wireload [$lib find_wireload $model_name]
|
||||
if {$wireload != "NULL"} {
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$lib_iter finish
|
||||
|
|
@ -3570,7 +3557,7 @@ proc set_wire_load_selection_group { args } {
|
|||
set lib [$lib_iter next]
|
||||
set selection [$lib find_wireload_selection $selection_name]
|
||||
if {$selection != "NULL"} {
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$lib_iter finish
|
||||
|
|
@ -3595,7 +3582,7 @@ proc set_voltage { args } {
|
|||
check_argc_eq1 "set_voltage" $args
|
||||
set max_case_voltage [lindex $args 0]
|
||||
check_float "max_case_voltage" $max_case_voltage
|
||||
|
||||
|
||||
set nets {}
|
||||
if { [info exists keys(-object_list)] } {
|
||||
set nets [get_nets_arg "-object_list" $keys(-object_list)]
|
||||
|
|
@ -3686,7 +3673,7 @@ proc set_pvt { args } {
|
|||
check_argc_eq1 "set_pvt" $args
|
||||
set insts [get_instances_error "instances" [lindex $args 0]]
|
||||
|
||||
if { $min_max == "all" } {
|
||||
if { $min_max == "min_max" } {
|
||||
set_pvt_min_max $insts "min" keys
|
||||
set_pvt_min_max $insts "max" keys
|
||||
} else {
|
||||
|
|
@ -382,7 +382,7 @@ WriteSdc::writeClocks() const
|
|||
{
|
||||
// Write clocks in the order they were defined because generated
|
||||
// clocks depend on master clocks having been previously defined.
|
||||
for (auto clk : sdc_->clocks_) {
|
||||
for (const auto clk : sdc_->clocks_) {
|
||||
if (clk->isGenerated())
|
||||
writeGeneratedClock(clk);
|
||||
else
|
||||
|
|
@ -852,8 +852,8 @@ void
|
|||
WriteSdc::writeClockSenses() const
|
||||
{
|
||||
Vector<PinClockPair> pin_clks;
|
||||
for (auto iter : sdc_->clk_sense_map_)
|
||||
pin_clks.push_back(iter.first);
|
||||
for (const auto& [pin_clk, sense] : sdc_->clk_sense_map_)
|
||||
pin_clks.push_back(pin_clk);
|
||||
|
||||
// Sort by pin/clk pair so regressions results are stable.
|
||||
sort(pin_clks, PinClockPairNameLess(sdc_network_));
|
||||
|
|
@ -937,10 +937,8 @@ ClockGroupLess::operator()(const ClockGroup *clk_group1,
|
|||
void
|
||||
WriteSdc::writeClockGroups() const
|
||||
{
|
||||
for (auto &name_groups : sdc_->clk_groups_name_map_) {
|
||||
ClockGroups *clk_groups = name_groups.second;
|
||||
for (const auto [name, clk_groups] : sdc_->clk_groups_name_map_)
|
||||
writeClockGroups(clk_groups);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1408,8 +1406,7 @@ void
|
|||
WriteSdc::writeDataChecks() const
|
||||
{
|
||||
Vector<DataCheck*> checks;
|
||||
for (auto pin_checks : sdc_->data_checks_to_map_) {
|
||||
DataCheckSet *checks1 = pin_checks.second;
|
||||
for (const auto [pin, checks1] : sdc_->data_checks_to_map_) {
|
||||
for (DataCheck *check : *checks1)
|
||||
checks.push_back(check);
|
||||
}
|
||||
|
|
@ -1512,9 +1509,7 @@ void
|
|||
WriteSdc::writeNetLoads() const
|
||||
{
|
||||
int corner_index = 0; // missing corner arg
|
||||
for (auto net_cap : sdc_->net_wire_cap_maps_[corner_index]) {
|
||||
const Net *net = net_cap.first;
|
||||
MinMaxFloatValues &caps = net_cap.second;
|
||||
for (const auto [net, caps] : sdc_->net_wire_cap_maps_[corner_index]) {
|
||||
float min_cap, max_cap;
|
||||
bool min_exists, max_exists;
|
||||
caps.value(MinMax::min(), min_cap, min_exists);
|
||||
|
|
@ -1725,10 +1720,8 @@ void
|
|||
WriteSdc::writeNetResistances() const
|
||||
{
|
||||
NetSeq nets;
|
||||
for (auto net_res : sdc_->netResistances()) {
|
||||
const Net *net = net_res.first;
|
||||
for (const auto [net, res] : sdc_->netResistances())
|
||||
nets.push_back(net);
|
||||
}
|
||||
sort(nets, NetPathNameLess(sdc_network_));
|
||||
for (const Net *net : nets) {
|
||||
float min_res, max_res;
|
||||
|
|
@ -1843,10 +1836,8 @@ void
|
|||
WriteSdc::sortedLogicValuePins(LogicValueMap &value_map,
|
||||
PinSeq &pins) const
|
||||
{
|
||||
for (auto pin_value : value_map) {
|
||||
const Pin *pin = pin_value.first;
|
||||
for (const auto [pin, value] : value_map)
|
||||
pins.push_back(pin);
|
||||
}
|
||||
// Sort pins.
|
||||
sort(pins, PinPathNameLess(sdc_network_));
|
||||
}
|
||||
|
|
@ -1860,9 +1851,7 @@ WriteSdc::writeDeratings() const
|
|||
if (factors)
|
||||
writeDerating(factors);
|
||||
|
||||
for (auto net_derating : sdc_->net_derating_factors_) {
|
||||
const Net *net = net_derating.first;
|
||||
DeratingFactorsNet *factors = net_derating.second;
|
||||
for (const auto [net, factors] : sdc_->net_derating_factors_) {
|
||||
WriteGetNet write_net(net, this);
|
||||
for (auto early_late : EarlyLate::range()) {
|
||||
writeDerating(factors, TimingDerateType::net_delay, early_late,
|
||||
|
|
@ -1870,16 +1859,12 @@ WriteSdc::writeDeratings() const
|
|||
}
|
||||
}
|
||||
|
||||
for (auto inst_derating : sdc_->inst_derating_factors_) {
|
||||
const Instance *inst = inst_derating.first;
|
||||
DeratingFactorsCell *factors = inst_derating.second;
|
||||
for (const auto [inst, factors] : sdc_->inst_derating_factors_) {
|
||||
WriteGetInstance write_inst(inst, this);
|
||||
writeDerating(factors, &write_inst);
|
||||
}
|
||||
|
||||
for (auto cell_derating : sdc_->cell_derating_factors_) {
|
||||
const LibertyCell *cell = cell_derating.first;
|
||||
DeratingFactorsCell *factors = cell_derating.second;
|
||||
for (const auto [cell, factors] : sdc_->cell_derating_factors_) {
|
||||
WriteGetLibCell write_cell(cell, this);
|
||||
writeDerating(factors, &write_cell);
|
||||
}
|
||||
|
|
@ -2029,12 +2014,10 @@ WriteSdc::writeVoltages() const
|
|||
gzprintf(stream_, "set_voltage %.3f\n", voltage_max);
|
||||
}
|
||||
|
||||
for (auto net_volt : sdc_->net_voltage_map_) {
|
||||
const Net *net = net_volt.first;
|
||||
MinMaxFloatValues &values = net_volt.second;
|
||||
values.value(MinMax::max(), voltage_max, exists_max);
|
||||
for (const auto& [net, volts] : sdc_->net_voltage_map_) {
|
||||
volts.value(MinMax::max(), voltage_max, exists_max);
|
||||
if (exists_max) {
|
||||
values.value(MinMax::min(), voltage_min, exists_min);
|
||||
volts.value(MinMax::min(), voltage_min, exists_min);
|
||||
if (exists_min)
|
||||
gzprintf(stream_, "set_voltage -object_list %s -min %.3f %.3f\n",
|
||||
sdc_network_->pathName(net),
|
||||
|
|
@ -2065,23 +2048,17 @@ WriteSdc::writeDesignRules() const
|
|||
void
|
||||
WriteSdc::writeMinPulseWidths() const
|
||||
{
|
||||
for (auto pin_widths : sdc_->pin_min_pulse_width_map_) {
|
||||
const Pin *pin = pin_widths.first;
|
||||
RiseFallValues *min_widths = pin_widths.second;
|
||||
for (const auto [pin, min_widths] : sdc_->pin_min_pulse_width_map_) {
|
||||
WriteGetPin write_obj(pin, false, this);
|
||||
writeMinPulseWidths(min_widths, write_obj);
|
||||
}
|
||||
|
||||
for (auto inst_widths : sdc_->inst_min_pulse_width_map_) {
|
||||
const Instance *inst = inst_widths.first;
|
||||
RiseFallValues *min_widths = inst_widths.second;
|
||||
for (const auto [inst, min_widths] : sdc_->inst_min_pulse_width_map_) {
|
||||
WriteGetInstance write_obj(inst, this);
|
||||
writeMinPulseWidths(min_widths, write_obj);
|
||||
}
|
||||
|
||||
for (auto clk_widths : sdc_->clk_min_pulse_width_map_) {
|
||||
const Clock *clk = clk_widths.first;
|
||||
RiseFallValues *min_widths = clk_widths.second;
|
||||
for (const auto [clk, min_widths] : sdc_->clk_min_pulse_width_map_) {
|
||||
WriteGetClock write_obj(clk, this);
|
||||
writeMinPulseWidths(min_widths, write_obj);
|
||||
}
|
||||
|
|
@ -2123,9 +2100,7 @@ WriteSdc::writeMinPulseWidth(const char *hi_low,
|
|||
void
|
||||
WriteSdc::writeLatchBorowLimits() const
|
||||
{
|
||||
for (auto pin_borrow : sdc_->pin_latch_borrow_limit_map_) {
|
||||
const Pin *pin = pin_borrow.first;
|
||||
float limit = pin_borrow.second;
|
||||
for (const auto [pin, limit] : sdc_->pin_latch_borrow_limit_map_) {
|
||||
gzprintf(stream_, "set_max_time_borrow ");
|
||||
writeTime(limit);
|
||||
gzprintf(stream_, " ");
|
||||
|
|
@ -2133,9 +2108,7 @@ WriteSdc::writeLatchBorowLimits() const
|
|||
gzprintf(stream_, "\n");
|
||||
}
|
||||
|
||||
for (auto inst_borrow : sdc_->inst_latch_borrow_limit_map_) {
|
||||
const Instance *inst = inst_borrow.first;
|
||||
float limit = inst_borrow.second;
|
||||
for (const auto [inst, limit] : sdc_->inst_latch_borrow_limit_map_) {
|
||||
gzprintf(stream_, "set_max_time_borrow ");
|
||||
writeTime(limit);
|
||||
gzprintf(stream_, " ");
|
||||
|
|
@ -2143,9 +2116,7 @@ WriteSdc::writeLatchBorowLimits() const
|
|||
gzprintf(stream_, "\n");
|
||||
}
|
||||
|
||||
for (auto clk_borrow : sdc_->clk_latch_borrow_limit_map_) {
|
||||
const Clock *clk = clk_borrow.first;
|
||||
float limit = clk_borrow.second;
|
||||
for (const auto [clk, limit] : sdc_->clk_latch_borrow_limit_map_) {
|
||||
gzprintf(stream_, "set_max_time_borrow ");
|
||||
writeTime(limit);
|
||||
gzprintf(stream_, " ");
|
||||
|
|
@ -2266,12 +2237,10 @@ WriteSdc::writeCapLimits(const MinMax *min_max,
|
|||
gzprintf(stream_, " [current_design]\n");
|
||||
}
|
||||
|
||||
for (auto port_limit : sdc_->port_cap_limit_map_) {
|
||||
const Port *port = port_limit.first;
|
||||
MinMaxFloatValues values = port_limit.second;
|
||||
for (const auto [port, limits] : sdc_->port_cap_limit_map_) {
|
||||
float cap;
|
||||
bool exists;
|
||||
values.value(min_max, cap, exists);
|
||||
limits.value(min_max, cap, exists);
|
||||
if (exists) {
|
||||
gzprintf(stream_, "%s ", cmd);
|
||||
writeCapacitance(cap);
|
||||
|
|
@ -2281,12 +2250,10 @@ WriteSdc::writeCapLimits(const MinMax *min_max,
|
|||
}
|
||||
}
|
||||
|
||||
for (auto pin_limit : sdc_->pin_cap_limit_map_) {
|
||||
const Pin *pin = pin_limit.first;
|
||||
MinMaxFloatValues values = pin_limit.second;
|
||||
for (const auto [pin, limits] : sdc_->pin_cap_limit_map_) {
|
||||
float cap;
|
||||
bool exists;
|
||||
values.value(min_max, cap, exists);
|
||||
limits.value(min_max, cap, exists);
|
||||
if (exists) {
|
||||
gzprintf(stream_, "%s ", cmd);
|
||||
writeCapacitance(cap);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
%module sdf
|
||||
|
||||
%{
|
||||
|
||||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2024, Parallax Software, Inc.
|
||||
//
|
||||
|
|
@ -18,6 +14,9 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
%module sdf
|
||||
|
||||
%{
|
||||
#include "sdf/SdfReader.hh"
|
||||
#include "sdf/ReportAnnotation.hh"
|
||||
#include "sdf/SdfWriter.hh"
|
||||
|
|
|
|||
|
|
@ -422,16 +422,20 @@ SdfReader::findPort(const Cell *cell,
|
|||
}
|
||||
|
||||
void
|
||||
SdfReader::timingCheck(TimingRole *role, SdfPortSpec *data_edge,
|
||||
SdfPortSpec *clk_edge, SdfTriple *triple)
|
||||
SdfReader::timingCheck(TimingRole *role,
|
||||
SdfPortSpec *data_edge,
|
||||
SdfPortSpec *clk_edge,
|
||||
SdfTriple *triple)
|
||||
{
|
||||
const char *data_port_name = data_edge->port();
|
||||
const char *clk_port_name = clk_edge->port();
|
||||
Cell *cell = network_->cell(instance_);
|
||||
Port *data_port = findPort(cell, data_port_name);
|
||||
Port *clk_port = findPort(cell, clk_port_name);
|
||||
if (data_port && clk_port)
|
||||
timingCheck1(role, data_port, data_edge, clk_port, clk_edge, triple);
|
||||
if (instance_) {
|
||||
const char *data_port_name = data_edge->port();
|
||||
const char *clk_port_name = clk_edge->port();
|
||||
Cell *cell = network_->cell(instance_);
|
||||
Port *data_port = findPort(cell, data_port_name);
|
||||
Port *clk_port = findPort(cell, clk_port_name);
|
||||
if (data_port && clk_port)
|
||||
timingCheck1(role, data_port, data_edge, clk_port, clk_edge, triple);
|
||||
}
|
||||
deletePortSpec(data_edge);
|
||||
deletePortSpec(clk_edge);
|
||||
deleteTriple(triple);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public:
|
|||
SdfWriter(StaState *sta);
|
||||
~SdfWriter();
|
||||
void write(const char *filename,
|
||||
Corner *corner,
|
||||
const Corner *corner,
|
||||
char sdf_divider,
|
||||
bool include_typ,
|
||||
int digits,
|
||||
|
|
@ -122,7 +122,7 @@ private:
|
|||
|
||||
void
|
||||
writeSdf(const char *filename,
|
||||
Corner *corner,
|
||||
const Corner *corner,
|
||||
char sdf_divider,
|
||||
bool include_typ,
|
||||
int digits,
|
||||
|
|
@ -151,7 +151,7 @@ SdfWriter::~SdfWriter()
|
|||
|
||||
void
|
||||
SdfWriter::write(const char *filename,
|
||||
Corner *corner,
|
||||
const Corner *corner,
|
||||
char sdf_divider,
|
||||
bool include_typ,
|
||||
int digits,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class Corner;
|
|||
|
||||
void
|
||||
writeSdf(const char *filename,
|
||||
Corner *corner,
|
||||
const Corner *corner,
|
||||
char divider,
|
||||
bool include_typ,
|
||||
int digits,
|
||||
|
|
|
|||
|
|
@ -16,30 +16,11 @@
|
|||
|
||||
#include "Bdd.hh"
|
||||
|
||||
#include "cudd.h"
|
||||
#include "StaConfig.hh"
|
||||
#include "Report.hh"
|
||||
#include "FuncExpr.hh"
|
||||
|
||||
#if CUDD
|
||||
#include "cudd.h"
|
||||
#else
|
||||
#include <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 {
|
||||
|
||||
Bdd::Bdd(const StaState *sta) :
|
||||
|
|
@ -168,10 +149,8 @@ Bdd::varIndexPort(int var_index)
|
|||
void
|
||||
Bdd::clearVarMap()
|
||||
{
|
||||
for (auto port_node : bdd_port_var_map_) {
|
||||
DdNode *var_node = port_node.second;
|
||||
for (const auto [port, var_node] : bdd_port_var_map_)
|
||||
Cudd_RecursiveDeref(cudd_mgr_, var_node);
|
||||
}
|
||||
bdd_port_var_map_.clear();
|
||||
bdd_var_idx_port_map_.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ BfsIterator::clear()
|
|||
Level level = first_level_;
|
||||
while (levelLessOrEqual(level, last_level_)) {
|
||||
VertexSeq &level_vertices = queue_[level];
|
||||
for (auto vertex : level_vertices) {
|
||||
for (Vertex *vertex : level_vertices) {
|
||||
if (vertex)
|
||||
vertex->setBfsInQueue(bfs_index_, false);
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ BfsIterator::reportEntries(const Network *network)
|
|||
VertexSeq &level_vertices = queue_[level];
|
||||
if (!level_vertices.empty()) {
|
||||
report_->reportLine("Level %d", level);
|
||||
for (auto vertex : level_vertices) {
|
||||
for (Vertex *vertex : level_vertices) {
|
||||
if (vertex)
|
||||
report_->reportLine(" %s", vertex->name(network));
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ void
|
|||
BfsIterator::deleteEntries(Level level)
|
||||
{
|
||||
VertexSeq &level_vertices = queue_[level];
|
||||
for (auto vertex : level_vertices) {
|
||||
for (Vertex *vertex : level_vertices) {
|
||||
if (vertex)
|
||||
vertex->setBfsInQueue(bfs_index_, false);
|
||||
}
|
||||
|
|
@ -281,7 +281,7 @@ BfsIterator::checkInQueue(Vertex *vertex)
|
|||
{
|
||||
Level level = vertex->level();
|
||||
if (static_cast<Level>(queue_.size()) > level) {
|
||||
for (auto v : queue_[level]) {
|
||||
for (Vertex *v : queue_[level]) {
|
||||
if (v == vertex) {
|
||||
if (vertex->bfsInQueue(bfs_index_))
|
||||
return;
|
||||
|
|
@ -308,7 +308,7 @@ BfsIterator::remove(Vertex *vertex)
|
|||
Level level = vertex->level();
|
||||
if (vertex->bfsInQueue(bfs_index_)
|
||||
&& static_cast<Level>(queue_.size()) > level) {
|
||||
for (auto &v : queue_[level]) {
|
||||
for (Vertex *v : queue_[level]) {
|
||||
if (v == vertex) {
|
||||
v = nullptr;
|
||||
vertex->setBfsInQueue(bfs_index_, false);
|
||||
|
|
|
|||
|
|
@ -411,7 +411,7 @@ MinPulseWidthCheck::width(const StaState *sta) const
|
|||
{
|
||||
return closeArrival(sta) + closeOffset(sta)
|
||||
- open_path_.arrival(sta)
|
||||
+ commonClkPessimism(sta);
|
||||
+ checkCrpr(sta);
|
||||
}
|
||||
|
||||
float
|
||||
|
|
@ -458,7 +458,7 @@ minPulseWidth(const Path *path,
|
|||
}
|
||||
|
||||
Crpr
|
||||
MinPulseWidthCheck::commonClkPessimism(const StaState *sta) const
|
||||
MinPulseWidthCheck::checkCrpr(const StaState *sta) const
|
||||
{
|
||||
CheckCrpr *check_crpr = sta->search()->checkCrpr();
|
||||
PathVertex close;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public:
|
|||
float closeOffset(const StaState *sta) const;
|
||||
const ClockEdge *openClkEdge(const StaState *sta) const;
|
||||
const ClockEdge *closeClkEdge(const StaState *sta) const;
|
||||
Crpr commonClkPessimism(const StaState *sta) const;
|
||||
Crpr checkCrpr(const StaState *sta) const;
|
||||
|
||||
protected:
|
||||
// Open path of the pulse.
|
||||
|
|
|
|||
|
|
@ -281,8 +281,7 @@ ClkSkews::findWorstClkSkew(const Corner *corner,
|
|||
clks.push_back(clk);
|
||||
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold, include_internal_latency);
|
||||
float worst_skew = 0.0;
|
||||
for (auto clk_skew_itr : skews) {
|
||||
ClkSkew &clk_skew = clk_skew_itr.second;
|
||||
for (const auto& [clk, clk_skew] : skews) {
|
||||
float skew = clk_skew.skew();
|
||||
if (abs(skew) > abs(worst_skew))
|
||||
worst_skew = skew;
|
||||
|
|
@ -318,9 +317,7 @@ ClkSkews::findClkSkew(ConstClockSeq &clks,
|
|||
|
||||
// Reduce skews from each register source.
|
||||
for (size_t i = 0; i < partial_skews.size(); i++) {
|
||||
for (auto clk_skew_itr : partial_skews[i]) {
|
||||
const Clock *clk = clk_skew_itr.first;
|
||||
auto partial_skew = clk_skew_itr.second;
|
||||
for (auto& [clk, partial_skew] : partial_skews[i]) {
|
||||
auto ins = skews.insert(std::make_pair(clk, partial_skew));
|
||||
if (!ins.second) {
|
||||
ClkSkew &final_skew = ins.first->second;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue