Merge remote-tracking branch 'parallax/master'

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

View File

@ -24,13 +24,12 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14)
cmake_policy(SET CMP0086 NEW)
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

View File

@ -85,15 +85,15 @@ The build dependency versions are show below. Other versions may
work, but these are the versions used for development.
```
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`).

View File

@ -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);
}

View File

@ -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"

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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() :

View File

@ -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);

View File

@ -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,

View File

@ -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.

View File

@ -1,902 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2023, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "CcsSimDelayCalc.hh"
#include <cmath> // abs
#include "Debug.hh"
#include "Units.hh"
#include "TimingArc.hh"
#include "Liberty.hh"
#include "Sdc.hh"
#include "Parasitics.hh"
#include "DcalcAnalysisPt.hh"
#include "Network.hh"
#include "Corner.hh"
#include "Graph.hh"
#include "GraphDelayCalc.hh"
#include "DmpDelayCalc.hh"
namespace sta {
using std::abs;
using std::make_shared;
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
// McGraw-Hill, Inc. New York, NY.
ArcDelayCalc *
makeCcsSimDelayCalc(StaState *sta)
{
return new CcsSimDelayCalc(sta);
}
CcsSimDelayCalc::CcsSimDelayCalc(StaState *sta) :
DelayCalcBase(sta),
dcalc_args_(nullptr),
load_pin_index_map_(network_),
dcalc_failed_(false),
pin_node_map_(network_),
make_waveforms_(false),
waveform_drvr_pin_(nullptr),
waveform_load_pin_(nullptr),
table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
{
}
CcsSimDelayCalc::~CcsSimDelayCalc()
{
delete table_dcalc_;
}
ArcDelayCalc *
CcsSimDelayCalc::copy()
{
return new CcsSimDelayCalc(this);
}
Parasitic *
CcsSimDelayCalc::findParasitic(const Pin *drvr_pin,
const RiseFall *,
const DcalcAnalysisPt *dcalc_ap)
{
const Corner *corner = dcalc_ap->corner();
Parasitic *parasitic = nullptr;
// set_load net has precidence over parasitics.
if (!sdc_->drvrPinHasWireCap(drvr_pin, corner)) {
const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
if (parasitics_->haveParasitics())
parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap);
}
return parasitic;
}
Parasitic *
CcsSimDelayCalc::reduceParasitic(const Parasitic *parasitic_network,
const Pin *,
const RiseFall *,
const DcalcAnalysisPt *)
{
return const_cast<Parasitic *>(parasitic_network);
}
ArcDcalcResult
CcsSimDelayCalc::inputPortDelay(const Pin *drvr_pin,
float in_slew,
const RiseFall *rf,
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap)
{
ArcDcalcResult dcalc_result(load_pin_index_map.size());
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
const Parasitic *pi_elmore = nullptr;
if (parasitic && parasitics_->isParasiticNetwork(parasitic)) {
const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt();
parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf,
dcalc_ap->corner(),
dcalc_ap->constraintMinMax(), ap);
pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
}
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew;
bool elmore_exists = false;
float elmore = 0.0;
if (pi_elmore)
parasitics_->findElmore(pi_elmore, load_pin, elmore, elmore_exists);
if (elmore_exists)
// Input port with no external driver.
dspfWireDelaySlew(load_pin, rf, in_slew, elmore, wire_delay, load_slew);
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay);
dcalc_result.setLoadSlew(load_idx, load_slew);
}
return dcalc_result;
}
ArcDcalcResult
CcsSimDelayCalc::gateDelay(const Pin *drvr_pin,
const TimingArc *arc,
const Slew &in_slew,
float load_cap,
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap)
{
ArcDcalcArgSeq dcalc_args;
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, parasitic);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_cap,
load_pin_index_map, dcalc_ap);
return dcalc_results[0];
}
ArcDcalcResultSeq
CcsSimDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap)
{
dcalc_args_ = &dcalc_args;
load_pin_index_map_ = load_pin_index_map;
drvr_count_ = dcalc_args.size();
load_cap_ = load_cap;
dcalc_ap_ = dcalc_ap;
drvr_rf_ = dcalc_args[0].arc()->toEdge()->asRiseFall();
dcalc_failed_ = false;
parasitic_network_ = dcalc_args[0].parasitic();
ArcDcalcResultSeq dcalc_results(drvr_count_);
size_t drvr_count = dcalc_args.size();
output_waveforms_.resize(drvr_count);
ref_time_.resize(drvr_count);
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
GateTableModel *table_model = gateTableModel(dcalc_arg.arc(), dcalc_ap);
if (table_model && dcalc_arg.parasitic()) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
float in_slew = delayAsFloat(dcalc_arg.inSlew());
if (output_waveforms
// Bounds check because extrapolating waveforms does not work for shit.
&& output_waveforms->slewAxis()->inBounds(in_slew)
&& output_waveforms->capAxis()->inBounds(load_cap)) {
output_waveforms_[drvr_idx] = output_waveforms;
ref_time_[drvr_idx] = output_waveforms->referenceTime(in_slew);
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
network_->libertyPort(dcalc_arg.drvrPin())->libertyCell()->name(),
drvr_rf_->asString());
LibertyCell *drvr_cell = dcalc_arg.arc()->to()->libertyCell();
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
bool vdd_exists;
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!vdd_exists)
report_->error(1720, "VDD not defined in library %s", drvr_library->name());
drvr_cell->ensureVoltageWaveforms();
if (drvr_idx == 0) {
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
}
}
else
dcalc_failed_ = true;
}
else
dcalc_failed_ = true;
}
if (dcalc_failed_) {
const Parasitic *parasitic_network = dcalc_args[0].parasitic();
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
Parasitic *pi_elmore = nullptr;
const Pin *drvr_pin = dcalc_arg.drvrPin();
if (parasitic_network) {
const ParasiticAnalysisPt *ap = dcalc_ap_->parasiticAnalysisPt();
parasitics_->reduceToPiElmore(parasitic_network, drvr_pin, drvr_rf_,
dcalc_ap_->corner(),
dcalc_ap_->constraintMinMax(), ap);
pi_elmore = parasitics_->findPiElmore(drvr_pin, drvr_rf_, ap);
dcalc_arg.setParasitic(pi_elmore);
}
}
dcalc_results = table_dcalc_->gateDelays(dcalc_args, load_cap,
load_pin_index_map, dcalc_ap);
}
else {
simulate(dcalc_args);
ArcDcalcArg &drvr_arg = dcalc_args[0];
const LibertyLibrary *drvr_library = drvr_arg.drvrLibrary();
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx];
const Pin *drvr_pin = dcalc_arg.drvrPin();
size_t drvr_node = pin_node_map_[drvr_pin];
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time_[drvr_idx];
Slew drvr_slew = abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]);
dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew);
debugPrint(debug_, "ccs_dcalc", 2,
"%s gate delay %s slew %s",
network_->pathName(drvr_pin),
delayAsString(gate_delay, this),
delayAsString(drvr_slew, this));
dcalc_result.setLoadCount(load_pin_index_map.size());
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
size_t load_node = pin_node_map_[load_pin];
ThresholdTimes &wire_times = threshold_times_[load_node];
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
Slew load_slew = abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
debugPrint(debug_, "ccs_dcalc", 2,
"load %s %s delay %s slew %s",
network_->pathName(load_pin),
drvr_rf_->asString(),
delayAsString(wire_delay, this),
delayAsString(load_slew, this));
thresholdAdjust(load_pin, drvr_library, drvr_rf_, wire_delay, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay);
dcalc_result.setLoadSlew(load_idx, load_slew);
}
}
}
return dcalc_results;
}
void
CcsSimDelayCalc::simulate(ArcDcalcArgSeq &dcalc_args)
{
const Pin *drvr_pin = dcalc_args[0].drvrPin();
LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
const MinMax *min_max = dcalc_ap_->delayMinMax();
drive_resistance_ = drvr_port->driveResistance(drvr_rf_, min_max);
initSim();
stampConductances();
// The conductance matrix does not change as long as the time step is constant.
// Factor stamping and LU decomposition of the conductance matrix
// outside of the simulation loop.
// Prevent copying of matrix.
conductances_.makeCompressed();
// LU factor conductances.
solver_.compute(conductances_);
for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
// Find initial ceff.
ceff_[drvr_idx] = load_cap_;
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
drvr_current_[drvr_idx] =
output_waveforms_[drvr_idx]->voltageCurrent(delayAsFloat(dcalc_arg.inSlew()),
ceff_[drvr_idx], 0.0);
}
// Initial time depends on ceff which impact delay, so use a sim step
// to find an initial ceff.
setCurrents();
voltages_ = solver_.solve(currents_);
updateCeffIdrvr();
initNodeVoltages();
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
double time_begin = output_waveforms_[0]->voltageTime(dcalc_args[0].inSlewFlt(),
ceff_[0], 0.0);
// Limit in case load voltage waveforms don't get to final value.
double time_end = time_begin + maxTime();
if (make_waveforms_)
recordWaveformStep(time_begin);
for (double time = time_begin; time <= time_end; time += time_step_) {
stampConductances();
conductances_.makeCompressed();
solver_.compute(conductances_);
setCurrents();
voltages_ = solver_.solve(currents_);
debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s",
delayAsString(time, this),
units_->capacitanceUnit()->asString(ceff_[0]),
voltages_[pin_node_map_[dcalc_args[0].drvrPin()]],
units_->currentUnit()->asString(drvr_current_[0], 4));
updateCeffIdrvr();
measureThresholds(time);
if (make_waveforms_)
recordWaveformStep(time);
bool loads_finished = true;
for (auto load_node1 : pin_node_map_) {
size_t load_node = load_node1.second;
if ((drvr_rf_ == RiseFall::rise()
&& voltages_[load_node] < vh_ + (vdd_ - vh_) * .5)
|| (drvr_rf_ == RiseFall::fall()
&& (voltages_[load_node] > vl_ * .5))) {
loads_finished = false;
break;
}
}
if (loads_finished)
break;
time_step_prev_ = time_step_;
// swap faster than copying with '='.
voltages_prev2_.swap(voltages_prev1_);
voltages_prev1_.swap(voltages_);
}
}
double
CcsSimDelayCalc::timeStep()
{
// Needs to use LTE for time step dynamic control.
return drive_resistance_ * load_cap_ * .02;
}
double
CcsSimDelayCalc::maxTime()
{
return (*dcalc_args_)[0].inSlewFlt()
+ (drive_resistance_ + resistance_sum_) * load_cap_ * 2;
}
void
CcsSimDelayCalc::initSim()
{
ceff_.resize(drvr_count_);
drvr_current_.resize(drvr_count_);
findNodeCount();
setOrder();
initNodeVoltages();
// time step required by initCapacitanceCurrents
time_step_ = time_step_prev_ = timeStep();
debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this));
// Reset waveform recording.
times_.clear();
drvr_voltages_.clear();
load_voltages_.clear();
measure_thresholds_ = {vl_, vth_, vh_};
}
void
CcsSimDelayCalc::findNodeCount()
{
includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network_);
coupling_cap_multiplier_ = 1.0;
node_capacitances_.clear();
pin_node_map_.clear();
node_index_map_.clear();
for (ParasiticNode *node : parasitics_->nodes(parasitic_network_)) {
if (!parasitics_->isExternal(node)) {
size_t node_idx = node_index_map_.size();
node_index_map_[node] = node_idx;
const Pin *pin = parasitics_->pin(node);
if (pin) {
pin_node_map_[pin] = node_idx;
debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu",
network_->pathName(pin),
node_idx);
}
double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
node_capacitances_.push_back(cap);
}
}
for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) {
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
ParasiticNode *node1 = parasitics_->node1(capacitor);
if (!parasitics_->isExternal(node1)) {
size_t node_idx = node_index_map_[node1];
node_capacitances_[node_idx] += cap;
}
ParasiticNode *node2 = parasitics_->node2(capacitor);
if (!parasitics_->isExternal(node2)) {
size_t node_idx = node_index_map_[node2];
node_capacitances_[node_idx] += cap;
}
}
node_count_ = node_index_map_.size();
}
float
CcsSimDelayCalc::pinCapacitance(ParasiticNode *node)
{
const Pin *pin = parasitics_->pin(node);
float pin_cap = 0.0;
if (pin) {
Port *port = network_->port(pin);
LibertyPort *lib_port = network_->libertyPort(port);
const Corner *corner = dcalc_ap_->corner();
const MinMax *cnst_min_max = dcalc_ap_->constraintMinMax();
if (lib_port) {
if (!includes_pin_caps_)
pin_cap = sdc_->pinCapacitance(pin, drvr_rf_, corner, cnst_min_max);
}
else if (network_->isTopLevelPort(pin))
pin_cap = sdc_->portExtCap(port, drvr_rf_, corner, cnst_min_max);
}
return pin_cap;
}
void
CcsSimDelayCalc::setOrder()
{
currents_.resize(node_count_);
voltages_.resize(node_count_);
voltages_prev1_.resize(node_count_);
voltages_prev2_.resize(node_count_);
// Matrix resize also zeros.
conductances_.resize(node_count_, node_count_);
threshold_times_.resize(node_count_);
}
void
CcsSimDelayCalc::initNodeVoltages()
{
double drvr_init_volt = (drvr_rf_ == RiseFall::rise()) ? 0.0 : vdd_;
for (size_t i = 0; i < node_count_; i++) {
voltages_[i] = drvr_init_volt;
voltages_prev1_[i] = drvr_init_volt;
voltages_prev2_[i] = drvr_init_volt;
}
}
void
CcsSimDelayCalc::simulateStep()
{
setCurrents();
voltages_ = solver_.solve(currents_);
}
void
CcsSimDelayCalc::stampConductances()
{
conductances_.setZero();
for (size_t node_idx = 0; node_idx < node_count_; node_idx++)
stampCapacitance(node_idx, node_capacitances_[node_idx]);
resistance_sum_ = 0.0;
for (ParasiticResistor *resistor : parasitics_->resistors(parasitic_network_)) {
ParasiticNode *node1 = parasitics_->node1(resistor);
ParasiticNode *node2 = parasitics_->node2(resistor);
// One commercial extractor creates resistors with identical from/to nodes.
if (node1 != node2) {
size_t node_idx1 = node_index_map_[node1];
size_t node_idx2 = node_index_map_[node2];
float resistance = parasitics_->value(resistor);
stampConductance(node_idx1, node_idx2, 1.0 / resistance);
resistance_sum_ += resistance;
}
}
}
// Grounded resistor.
void
CcsSimDelayCalc::stampConductance(size_t n1,
double g)
{
conductances_.coeffRef(n1, n1) += g;
}
// Floating resistor.
void
CcsSimDelayCalc::stampConductance(size_t n1,
size_t n2,
double g)
{
conductances_.coeffRef(n1, n1) += g;
conductances_.coeffRef(n2, n2) += g;
conductances_.coeffRef(n1, n2) -= g;
conductances_.coeffRef(n2, n1) -= g;
}
// Grounded capacitance.
void
CcsSimDelayCalc::stampCapacitance(size_t n1,
double cap)
{
double g = cap * 2.0 / time_step_;
stampConductance(n1, g);
}
// Floating capacitance.
void
CcsSimDelayCalc::stampCapacitance(size_t n1,
size_t n2,
double cap)
{
double g = cap * 2.0 / time_step_;
stampConductance(n1, n2, g);
}
////////////////////////////////////////////////////////////////
void
CcsSimDelayCalc::setCurrents()
{
currents_.setZero(node_count_);
for (size_t i = 0; i < drvr_count_; i++) {
size_t drvr_node = pin_node_map_[(*dcalc_args_)[i].drvrPin()];
insertCurrentSrc(drvr_node, drvr_current_[i]);
}
for (size_t node_idx = 0; node_idx < node_count_; node_idx++)
insertCapCurrentSrc(node_idx, node_capacitances_[node_idx]);
}
void
CcsSimDelayCalc::insertCapCurrentSrc(size_t n1,
double cap)
{
// Direct implementation of figure 4.11 in
// “Electronic Circuit & System Simulation Methods” allowing for time
// step changes.
// double g0 = 2.0 * cap / time_step_;
// double g1 = 2.0 * cap / time_step_prev_;
// double dv = voltages_prev2_[n1] - voltages_prev1_[n1];
// double ieq_prev = cap * dv / time_step_ + g0 * voltages_prev1_[n1];
// double i_cap = (g0 + g1) * voltages_prev1_[n1] - ieq_prev;
// Above simplified.
// double i_cap
// = cap / time_step_ * voltages_prev1_[n1]
// + 2.0 * cap / time_step_prev_ * voltages_prev1_[n1]
// - cap / time_step_ * voltages_prev2_[n1];
// Simplified for constant time step.
double i_cap
= 3.0 * cap / time_step_ * voltages_prev1_[n1]
- cap / time_step_ * voltages_prev2_[n1];
insertCurrentSrc(n1, i_cap);
}
void
CcsSimDelayCalc::insertCapaCurrentSrc(size_t n1,
size_t n2,
double cap)
{
double g0 = 2.0 * cap / time_step_;
double g1 = 2.0 * cap / time_step_prev_;
double dv = (voltages_prev2_[n1] - voltages_prev2_[n2])
- (voltages_prev1_[n1] - voltages_prev1_[n2]);
double ieq_prev = cap * dv / time_step_ + g0*(voltages_prev1_[n1]-voltages_prev1_[n2]);
double i_cap = (g0 + g1) * (voltages_prev1_[n1] - voltages_prev1_[n2]) - ieq_prev;
insertCurrentSrc(n1, n2, i_cap);
}
void
CcsSimDelayCalc::insertCurrentSrc(size_t n1,
double current)
{
currents_.coeffRef(n1) += current;
}
void
CcsSimDelayCalc::insertCurrentSrc(size_t n1,
size_t n2,
double current)
{
currents_.coeffRef(n1) += current;
currents_.coeffRef(n2) -= current;
}
void
CcsSimDelayCalc::updateCeffIdrvr()
{
for (size_t i = 0; i < drvr_count_; i++) {
size_t drvr_node = pin_node_map_[(*dcalc_args_)[i].drvrPin()];
double dv = voltages_[drvr_node] - voltages_prev1_[drvr_node];
if (drvr_rf_ == RiseFall::rise()) {
if (drvr_current_[i] != 0.0
&& dv > 0.0) {
double ceff = drvr_current_[i] * time_step_ / dv;
if (output_waveforms_[i]->capAxis()->inBounds(ceff))
ceff_[i] = ceff;
}
double v = voltages_[drvr_node];
if (voltages_[drvr_node] > (vdd_ - .01))
// Whoa partner. Head'n for the weeds.
drvr_current_[i] = 0.0;
else
drvr_current_[i] =
output_waveforms_[i]->voltageCurrent((*dcalc_args_)[i].inSlewFlt(),
ceff_[i], v);
}
else {
if (drvr_current_[i] != 0.0
&& dv < 0.0) {
double ceff = drvr_current_[i] * time_step_ / dv;
if (output_waveforms_[i]->capAxis()->inBounds(ceff))
ceff_[i] = ceff;
}
double v = vdd_ - voltages_[drvr_node];
if (voltages_[drvr_node] < 0.01)
// Whoa partner. Head'n for the weeds.
drvr_current_[i] = 0.0;
else
drvr_current_[i] =
output_waveforms_[i]->voltageCurrent((*dcalc_args_)[i].inSlewFlt(),
ceff_[i], v);
}
}
}
////////////////////////////////////////////////////////////////
void
CcsSimDelayCalc::measureThresholds(double time)
{
for (auto pin_node1 : pin_node_map_) {
size_t pin_node = pin_node1.second;
measureThresholds(time, pin_node);
}
}
void
CcsSimDelayCalc::measureThresholds(double time,
size_t n)
{
double v = voltages_[n];
double v_prev = voltages_prev1_[n];
for (size_t m = 0; m < measure_threshold_count_; m++) {
double th = measure_thresholds_[m];
if ((v_prev < th && th <= v)
|| (v_prev > th && th >= v)) {
double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s",
n,
th,
delayAsString(t_cross, this));
threshold_times_[n][m] = t_cross;
}
}
}
void
CcsSimDelayCalc::recordWaveformStep(double time)
{
times_.push_back(time);
size_t drvr_node = pin_node_map_[waveform_drvr_pin_];
drvr_voltages_.push_back(voltages_[drvr_node]);
if (waveform_load_pin_) {
size_t load_node = pin_node_map_[waveform_load_pin_];
load_voltages_.push_back(voltages_[load_node]);
}
}
////////////////////////////////////////////////////////////////
string
CcsSimDelayCalc::reportGateDelay(const Pin *drvr_pin,
const TimingArc *arc,
const Slew &in_slew,
float load_cap,
const Parasitic *,
const LoadPinIndexMap &,
const DcalcAnalysisPt *dcalc_ap,
int digits)
{
GateTimingModel *model = gateModel(arc, dcalc_ap);
if (model) {
float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap,
false, digits);
}
return "";
}
////////////////////////////////////////////////////////////////
// Waveform accessors for swig/tcl.
Table1
CcsSimDelayCalc::drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max)
{
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, nullptr, corner, min_max);
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(times_));
Table1 waveform(new FloatSeq(drvr_voltages_), time_axis);
return waveform;
}
Table1
CcsSimDelayCalc::loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
{
makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, load_pin, corner, min_max);
TableAxisPtr time_axis = make_shared<TableAxis>(TableAxisVariable::time,
new FloatSeq(times_));
Table1 waveform(new FloatSeq(load_voltages_), time_axis);
return waveform;
}
Table1
CcsSimDelayCalc::inputWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Corner *corner,
const MinMax *min_max)
{
LibertyPort *port = network_->libertyPort(in_pin);
if (port) {
DriverWaveform *driver_waveform = port->driverWaveform(in_rf);
const Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
float in_slew = delayAsFloat(graph_->slew(in_vertex, in_rf, dcalc_ap->index()));
LibertyLibrary *library = port->libertyLibrary();
float vdd;
bool vdd_exists;
library->supplyVoltage("VDD", vdd, vdd_exists);
if (!vdd_exists)
report_->error(1721, "VDD not defined in library %s", library->name());
Table1 in_waveform = driver_waveform->waveform(in_slew);
// Scale the waveform from 0:vdd.
FloatSeq *scaled_values = new FloatSeq;
for (float value : *in_waveform.values())
scaled_values->push_back(value * vdd);
return Table1(scaled_values, in_waveform.axis1ptr());
}
return Table1();
}
void
CcsSimDelayCalc::makeWaveforms(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max)
{
Edge *edge;
const TimingArc *arc;
graph_->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc);
if (arc) {
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
const Parasitic *parasitic = findParasitic(drvr_pin, drvr_rf, dcalc_ap);
if (parasitic) {
make_waveforms_ = true;
waveform_drvr_pin_ = drvr_pin;
waveform_load_pin_ = load_pin;
Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
graph_delay_calc_->findDriverArcDelays(drvr_vertex, edge, arc, dcalc_ap, this);
make_waveforms_ = false;
waveform_drvr_pin_ = nullptr;
waveform_load_pin_ = nullptr;
}
}
}
////////////////////////////////////////////////////////////////
void
CcsSimDelayCalc::reportMatrix(const char *name,
MatrixSd &matrix)
{
report_->reportLine("%s", name);
reportMatrix(matrix);
}
void
CcsSimDelayCalc::reportMatrix(const char *name,
MatrixXd &matrix)
{
report_->reportLine("%s", name);
reportMatrix(matrix);
}
void
CcsSimDelayCalc::reportMatrix(const char *name,
VectorXd &matrix)
{
report_->reportLine("%s", name);
reportMatrix(matrix);
}
void
CcsSimDelayCalc::reportVector(const char *name,
vector<double> &matrix)
{
report_->reportLine("%s", name);
reportVector(matrix);
}
void
CcsSimDelayCalc::reportMatrix(MatrixSd &matrix)
{
for (Index i = 0; i < matrix.rows(); i++) {
string line = "| ";
for (Index j = 0; j < matrix.cols(); j++) {
string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
line += entry;
line += " ";
}
line += "|";
report_->reportLineString(line);
}
}
void
CcsSimDelayCalc::reportMatrix(MatrixXd &matrix)
{
for (Index i = 0; i < matrix.rows(); i++) {
string line = "| ";
for (Index j = 0; j < matrix.cols(); j++) {
string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
line += entry;
line += " ";
}
line += "|";
report_->reportLineString(line);
}
}
void
CcsSimDelayCalc::reportMatrix(VectorXd &matrix)
{
string line = "| ";
for (Index i = 0; i < matrix.rows(); i++) {
string entry = stdstrPrint("%10.3e", matrix.coeff(i));
line += entry;
line += " ";
}
line += "|";
report_->reportLineString(line);
}
void
CcsSimDelayCalc::reportVector(vector<double> &matrix)
{
string line = "| ";
for (size_t i = 0; i < matrix.size(); i++) {
string entry = stdstrPrint("%10.3e", matrix[i]);
line += entry;
line += " ";
}
line += "|";
report_->reportLineString(line);
}
} // namespace

View File

@ -1,238 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2023, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <vector>
#include <Eigen/SparseCore>
#include <Eigen/SparseLU>
#include "LumpedCapDelayCalc.hh"
#include "ArcDcalcWaveforms.hh"
namespace sta {
class ArcDelayCalc;
class StaState;
class Corner;
using std::vector;
using std::array;
using Eigen::MatrixXd;
using Eigen::MatrixXcd;
using Eigen::VectorXd;
using Eigen::SparseMatrix;
using Eigen::Index;
using Eigen::SparseLU;
typedef Map<const Pin*, size_t, PinIdLess> PinNodeMap;
typedef Map<const ParasiticNode*, size_t> NodeIndexMap;
typedef Map<const Pin*, size_t> PortIndexMap;
typedef SparseMatrix<double> MatrixSd;
ArcDelayCalc *
makeCcsSimDelayCalc(StaState *sta);
class CcsSimDelayCalc : public DelayCalcBase, public ArcDcalcWaveforms
{
public:
CcsSimDelayCalc(StaState *sta);
~CcsSimDelayCalc();
ArcDelayCalc *copy() override;
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override;
Parasitic *reduceParasitic(const Parasitic *parasitic_network,
const Pin *drvr_pin,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResult inputPortDelay(const Pin *drvr_pin,
float in_slew,
const RiseFall *rf,
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResult gateDelay(const Pin *drvr_pin,
const TimingArc *arc,
const Slew &in_slew,
float load_cap,
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
float load_cap,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap) override;
string reportGateDelay(const Pin *drvr_pin,
const TimingArc *arc,
const Slew &in_slew,
float load_cap,
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const DcalcAnalysisPt *dcalc_ap,
int digits) override;
Table1 inputWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Corner *corner,
const MinMax *min_max) override;
Table1 drvrWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Corner *corner,
const MinMax *min_max) override;
Table1 loadWaveform(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max) override;
protected:
void simulate(ArcDcalcArgSeq &dcalc_args);
virtual double maxTime();
virtual double timeStep();
void updateCeffIdrvr();
void initSim();
void findLoads();
virtual void findNodeCount();
void setOrder();
void initNodeVoltages();
void simulateStep();
virtual void stampConductances();
void stampConductance(size_t n1,
double g);
void stampConductance(size_t n1,
size_t n2,
double g);
void stampCapacitance(size_t n1,
double cap);
void stampCapacitance(size_t n1,
size_t n2,
double cap);
float pinCapacitance(ParasiticNode *node);
virtual void setCurrents();
void insertCapCurrentSrc(size_t n1,
double cap);
void insertCapaCurrentSrc(size_t n1,
size_t n2,
double cap);
void insertCurrentSrc(size_t n1,
double current);
void insertCurrentSrc(size_t n1,
size_t n2,
double current);
void measureThresholds(double time);
void measureThresholds(double time,
size_t i);
void loadDelaySlew(const Pin *load_pin,
// Return values.
ArcDelay &delay,
Slew &slew);
void recordWaveformStep(double time);
void makeWaveforms(const Pin *in_pin,
const RiseFall *in_rf,
const Pin *drvr_pin,
const RiseFall *drvr_rf,
const Pin *load_pin,
const Corner *corner,
const MinMax *min_max);
void reportMatrix(const char *name,
MatrixSd &matrix);
void reportMatrix(const char *name,
MatrixXd &matrix);
void reportMatrix(const char *name,
VectorXd &matrix);
void reportVector(const char *name,
vector<double> &matrix);
void reportMatrix(MatrixSd &matrix);
void reportMatrix(MatrixXd &matrix);
void reportMatrix(VectorXd &matrix);
void reportVector(vector<double> &matrix);
ArcDcalcArgSeq *dcalc_args_;
size_t drvr_count_;
float load_cap_;
const DcalcAnalysisPt *dcalc_ap_;
const Parasitic *parasitic_network_;
const RiseFall *drvr_rf_;
// Tmp for gateDelay/loadDelay api.
ArcDcalcResult dcalc_result_;
LoadPinIndexMap load_pin_index_map_;
bool dcalc_failed_;
size_t node_count_; // Parasitic network node count
PinNodeMap pin_node_map_; // Parasitic pin -> array index
NodeIndexMap node_index_map_; // Parasitic node -> array index
vector<OutputWaveforms*> output_waveforms_;
vector<float> ref_time_;
double drive_resistance_;
double resistance_sum_;
vector<double> node_capacitances_;
bool includes_pin_caps_;
float coupling_cap_multiplier_;
// Indexed by driver index.
vector<double> ceff_;
vector<double> drvr_current_;
double time_step_;
double time_step_prev_;
// I = GV
// currents_ = conductances_ * voltages_
VectorXd currents_;
MatrixSd conductances_;
VectorXd voltages_;
VectorXd voltages_prev1_;
VectorXd voltages_prev2_;
SparseLU<MatrixSd> solver_;
// Waveform recording.
bool make_waveforms_;
const Pin *waveform_drvr_pin_;
const Pin *waveform_load_pin_;
FloatSeq drvr_voltages_;
FloatSeq load_voltages_;
FloatSeq times_;
size_t drvr_idx_;
float vdd_;
float vth_;
float vl_;
float vh_;
static constexpr size_t threshold_vl = 0;
static constexpr size_t threshold_vth = 1;
static constexpr size_t threshold_vh = 2;
static constexpr size_t measure_threshold_count_ = 3;
typedef array<double, measure_threshold_count_> ThresholdTimes;
// Vl Vth Vh
ThresholdTimes measure_thresholds_;
// Indexed by node number.
vector<ThresholdTimes> threshold_times_;
// Delay calculator to use when ccs waveforms are missing from liberty.
ArcDelayCalc *table_dcalc_;
using ArcDelayCalc::reduceParasitic;
};
} // namespacet

View File

@ -23,7 +23,6 @@
#include "DmpDelayCalc.hh"
#include "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;
}

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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.

View File

@ -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_) {

View File

@ -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,

View File

@ -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) {

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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));
}

View File

@ -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);
};

View File

@ -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)

View File

@ -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,

View File

@ -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 *)
{

View File

@ -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,

View File

@ -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
-------------------------

View File

@ -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");

Binary file not shown.

Binary file not shown.

View File

@ -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;
}

459
graph/Graph.i Normal file
View File

@ -0,0 +1,459 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
%module graph
%{
#include "Graph.hh"
#include "FuncExpr.hh"
#include "TimingRole.hh"
#include "Liberty.hh"
#include "Network.hh"
#include "Clock.hh"
#include "Corner.hh"
#include "Search.hh"
#include "Sta.hh"
namespace sta {
Graph *
cmdGraph();
} // namespace
using namespace sta;
%}
////////////////////////////////////////////////////////////////
//
// Empty class definitions to make swig happy.
// Private constructor/destructor so swig doesn't emit them.
//
////////////////////////////////////////////////////////////////
class Vertex
{
private:
Vertex();
~Vertex();
};
class Edge
{
private:
Edge();
~Edge();
};
class VertexIterator
{
private:
VertexIterator();
~VertexIterator();
};
class VertexInEdgeIterator
{
private:
VertexInEdgeIterator();
~VertexInEdgeIterator();
};
class VertexOutEdgeIterator
{
private:
VertexOutEdgeIterator();
~VertexOutEdgeIterator();
};
%inline %{
int
graph_vertex_count()
{
return cmdGraph()->vertexCount();
}
int
graph_edge_count()
{
return cmdGraph()->edgeCount();
}
int
graph_arc_count()
{
return cmdGraph()->arcCount();
}
VertexIterator *
vertex_iterator()
{
return new VertexIterator(cmdGraph());
}
void
set_arc_delay(Edge *edge,
TimingArc *arc,
const Corner *corner,
const MinMaxAll *min_max,
float delay)
{
cmdGraph();
Sta::sta()->setArcDelay(edge, arc, corner, min_max, delay);
}
void
set_annotated_slew(Vertex *vertex,
const Corner *corner,
const MinMaxAll *min_max,
const RiseFallBoth *rf,
float slew)
{
cmdGraph();
Sta::sta()->setAnnotatedSlew(vertex, corner, min_max, rf, slew);
}
// Remove all delay and slew annotations.
void
remove_delay_slew_annotations()
{
cmdGraph();
Sta::sta()->removeDelaySlewAnnotations();
}
%} // inline
////////////////////////////////////////////////////////////////
//
// Object Methods
//
////////////////////////////////////////////////////////////////
%extend Vertex {
Pin *pin() { return self->pin(); }
bool is_bidirect_driver() { return self->isBidirectDriver(); }
int level() { return Sta::sta()->vertexLevel(self); }
int tag_group_index() { return self->tagGroupIndex(); }
Slew
slew(const RiseFall *rf,
const MinMax *min_max)
{
Sta *sta = Sta::sta();
return sta->vertexSlew(self, rf, min_max);
}
Slew
slew_corner(const RiseFall *rf,
const Corner *corner,
const MinMax *min_max)
{
Sta *sta = Sta::sta();
return sta->vertexSlew(self, rf, corner, min_max);
}
VertexOutEdgeIterator *
out_edge_iterator()
{
return new VertexOutEdgeIterator(self, Sta::sta()->graph());
}
VertexInEdgeIterator *
in_edge_iterator()
{
return new VertexInEdgeIterator(self, Sta::sta()->graph());
}
FloatSeq
arrivals_clk(const RiseFall *rf,
Clock *clk,
const RiseFall *clk_rf)
{
Sta *sta = Sta::sta();
FloatSeq arrivals;
const ClockEdge *clk_edge = nullptr;
if (clk)
clk_edge = clk->edge(clk_rf);
for (auto path_ap : sta->corners()->pathAnalysisPts()) {
arrivals.push_back(delayAsFloat(sta->vertexArrival(self, rf, clk_edge,
path_ap, nullptr)));
}
return arrivals;
}
StringSeq
arrivals_clk_delays(const RiseFall *rf,
Clock *clk,
const RiseFall *clk_rf,
int digits)
{
Sta *sta = Sta::sta();
StringSeq arrivals;
const ClockEdge *clk_edge = nullptr;
if (clk)
clk_edge = clk->edge(clk_rf);
for (auto path_ap : sta->corners()->pathAnalysisPts()) {
arrivals.push_back(delayAsString(sta->vertexArrival(self, rf, clk_edge,
path_ap, nullptr),
sta, digits));
}
return arrivals;
}
FloatSeq
requireds_clk(const RiseFall *rf,
Clock *clk,
const RiseFall *clk_rf)
{
Sta *sta = Sta::sta();
FloatSeq reqs;
const ClockEdge *clk_edge = nullptr;
if (clk)
clk_edge = clk->edge(clk_rf);
for (auto path_ap : sta->corners()->pathAnalysisPts()) {
reqs.push_back(delayAsFloat(sta->vertexRequired(self, rf, clk_edge,
path_ap)));
}
return reqs;
}
StringSeq
requireds_clk_delays(const RiseFall *rf,
Clock *clk,
const RiseFall *clk_rf,
int digits)
{
Sta *sta = Sta::sta();
StringSeq reqs;
const ClockEdge *clk_edge = nullptr;
if (clk)
clk_edge = clk->edge(clk_rf);
for (auto path_ap : sta->corners()->pathAnalysisPts()) {
reqs.push_back(delayAsString(sta->vertexRequired(self, rf, clk_edge, path_ap),
sta, digits));
}
return reqs;
}
Slack
slack(MinMax *min_max)
{
Sta *sta = Sta::sta();
return sta->vertexSlack(self, min_max);
}
FloatSeq
slacks(RiseFall *rf)
{
Sta *sta = Sta::sta();
FloatSeq slacks;
for (auto path_ap : sta->corners()->pathAnalysisPts()) {
slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, path_ap)));
}
return slacks;
}
// Slack with respect to a clock rise/fall edge.
FloatSeq
slacks_clk(const RiseFall *rf,
Clock *clk,
const RiseFall *clk_rf)
{
Sta *sta = Sta::sta();
FloatSeq slacks;
const ClockEdge *clk_edge = nullptr;
if (clk)
clk_edge = clk->edge(clk_rf);
for (auto path_ap : sta->corners()->pathAnalysisPts()) {
slacks.push_back(delayAsFloat(sta->vertexSlack(self, rf, clk_edge,
path_ap)));
}
return slacks;
}
StringSeq
slacks_clk_delays(const RiseFall *rf,
Clock *clk,
const RiseFall *clk_rf,
int digits)
{
Sta *sta = Sta::sta();
StringSeq slacks;
const ClockEdge *clk_edge = nullptr;
if (clk)
clk_edge = clk->edge(clk_rf);
for (auto path_ap : sta->corners()->pathAnalysisPts()) {
slacks.push_back(delayAsString(sta->vertexSlack(self, rf, clk_edge,
path_ap),
sta, digits));
}
return slacks;
}
VertexPathIterator *
path_iterator(const RiseFall *rf,
const MinMax *min_max)
{
return Sta::sta()->vertexPathIterator(self, rf, min_max);
}
bool
has_downstream_clk_pin()
{
return self->hasDownstreamClkPin();
}
bool
is_clock()
{
Sta *sta = Sta::sta();
Search *search = sta->search();
return search->isClock(self);
}
bool is_disabled_constraint() { return self->isDisabledConstraint(); }
} // Vertex methods
%extend Edge {
Vertex *from() { return self->from(Sta::sta()->graph()); }
Vertex *to() { return self->to(Sta::sta()->graph()); }
Pin *from_pin() { return self->from(Sta::sta()->graph())->pin(); }
Pin *to_pin() { return self->to(Sta::sta()->graph())->pin(); }
TimingRole *role() { return self->role(); }
const char *sense() { return timingSenseString(self->sense()); }
TimingArcSeq &
timing_arcs() { return self->timingArcSet()->arcs(); }
bool is_disabled_loop() { return Sta::sta()->isDisabledLoop(self); }
bool is_disabled_constraint() { return Sta::sta()->isDisabledConstraint(self);}
bool is_disabled_constant() { return Sta::sta()->isDisabledConstant(self); }
bool is_disabled_cond_default()
{ return Sta::sta()->isDisabledCondDefault(self); }
PinSet
disabled_constant_pins() { return Sta::sta()->disabledConstantPins(self); }
bool is_disabled_bidirect_inst_path()
{ return Sta::sta()->isDisabledBidirectInstPath(self); }
bool is_disabled_bidirect_net_path()
{ return Sta::sta()->isDisabledBidirectNetPath(self); }
bool is_disabled_preset_clear()
{ return Sta::sta()->isDisabledPresetClr(self); }
const char *
sim_timing_sense(){return timingSenseString(Sta::sta()->simTimingSense(self));}
FloatSeq
arc_delays(TimingArc *arc)
{
Sta *sta = Sta::sta();
FloatSeq delays;
for (auto dcalc_ap : sta->corners()->dcalcAnalysisPts())
delays.push_back(delayAsFloat(sta->arcDelay(self, arc, dcalc_ap)));
return delays;
}
StringSeq
arc_delay_strings(TimingArc *arc,
int digits)
{
Sta *sta = Sta::sta();
StringSeq delays;
for (auto dcalc_ap : sta->corners()->dcalcAnalysisPts())
delays.push_back(delayAsString(sta->arcDelay(self, arc, dcalc_ap),
sta, digits));
return delays;
}
bool
delay_annotated(TimingArc *arc,
const Corner *corner,
const MinMax *min_max)
{
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
return Sta::sta()->arcDelayAnnotated(self, arc, dcalc_ap);
}
float
arc_delay(TimingArc *arc,
const Corner *corner,
const MinMax *min_max)
{
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
return delayAsFloat(Sta::sta()->arcDelay(self, arc, dcalc_ap));
}
const char *
cond()
{
FuncExpr *cond = self->timingArcSet()->cond();
if (cond)
return cond->asString();
else
return nullptr;
}
const char *
mode_name()
{
return self->timingArcSet()->modeName();
}
const char *
mode_value()
{
return self->timingArcSet()->modeValue();
}
const char *
latch_d_to_q_en()
{
if (self->role() == TimingRole::latchDtoQ()) {
Sta *sta = Sta::sta();
const Network *network = sta->cmdNetwork();
const Graph *graph = sta->graph();
Pin *from_pin = self->from(graph)->pin();
Instance *inst = network->instance(from_pin);
LibertyCell *lib_cell = network->libertyCell(inst);
TimingArcSet *d_q_set = self->timingArcSet();
const LibertyPort *enable_port;
const FuncExpr *enable_func;
const RiseFall *enable_rf;
lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf);
if (enable_port)
return stringPrintTmp("%s %s", enable_port->name(), enable_rf->asString());
}
return "";
}
} // Edge methods
%extend VertexIterator {
bool has_next() { return self->hasNext(); }
Vertex *next() { return self->next(); }
void finish() { delete self; }
}
%extend VertexInEdgeIterator {
bool has_next() { return self->hasNext(); }
Edge *next() { return self->next(); }
void finish() { delete self; }
}
%extend VertexOutEdgeIterator {
bool has_next() { return self->hasNext(); }
Edge *next() { return self->next(); }
void finish() { delete self; }
}

View File

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

View File

@ -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;

View File

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

View File

@ -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>

View File

@ -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_;
};

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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:

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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_;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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)

542
liberty/Liberty.i Normal file
View File

@ -0,0 +1,542 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
%module liberty
%{
#include "PortDirection.hh"
#include "Liberty.hh"
#include "EquivCells.hh"
#include "LibertyWriter.hh"
#include "Sta.hh"
using namespace sta;
%}
////////////////////////////////////////////////////////////////
//
// Empty class definitions to make swig happy.
// Private constructor/destructor so swig doesn't emit them.
//
////////////////////////////////////////////////////////////////
class LibertyLibrary
{
private:
LibertyLibrary();
~LibertyLibrary();
};
class LibertyLibraryIterator
{
private:
LibertyLibraryIterator();
~LibertyLibraryIterator();
};
class LibertyCell
{
private:
LibertyCell();
~LibertyCell();
};
class LibertyPort
{
private:
LibertyPort();
~LibertyPort();
};
class LibertyCellPortIterator
{
private:
LibertyCellPortIterator();
~LibertyCellPortIterator();
};
class LibertyPortMemberIterator
{
private:
LibertyPortMemberIterator();
~LibertyPortMemberIterator();
};
class TimingArcSet
{
private:
TimingArcSet();
~TimingArcSet();
};
class TimingArc
{
private:
TimingArc();
~TimingArc();
};
class Wireload
{
private:
Wireload();
~Wireload();
};
class WireloadSelection
{
private:
WireloadSelection();
~WireloadSelection();
};
%inline %{
bool
read_liberty_cmd(char *filename,
Corner *corner,
const MinMaxAll *min_max,
bool infer_latches)
{
Sta *sta = Sta::sta();
LibertyLibrary *lib = sta->readLiberty(filename, corner, min_max, infer_latches);
return (lib != nullptr);
}
void
write_liberty_cmd(LibertyLibrary *library,
char *filename)
{
writeLiberty(library, filename, Sta::sta());
}
void
make_equiv_cells(LibertyLibrary *lib)
{
LibertyLibrarySeq libs;
libs.push_back(lib);
Sta::sta()->makeEquivCells(&libs, nullptr);
}
LibertyCellSeq *
find_equiv_cells(LibertyCell *cell)
{
return Sta::sta()->equivCells(cell);
}
bool
equiv_cells(LibertyCell *cell1,
LibertyCell *cell2)
{
return sta::equivCells(cell1, cell2);
}
bool
equiv_cell_ports(LibertyCell *cell1,
LibertyCell *cell2)
{
return equivCellPorts(cell1, cell2);
}
bool
equiv_cell_timing_arcs(LibertyCell *cell1,
LibertyCell *cell2)
{
return equivCellTimingArcSets(cell1, cell2);
}
LibertyCellSeq *
find_library_buffers(LibertyLibrary *library)
{
return library->buffers();
}
const char *
liberty_port_direction(const LibertyPort *port)
{
return port->direction()->name();
}
bool
liberty_supply_exists(const char *supply_name)
{
auto network = Sta::sta()->network();
auto lib = network->defaultLibertyLibrary();
return lib && lib->supplyExists(supply_name);
}
LibertyLibraryIterator *
liberty_library_iterator()
{
return cmdNetwork()->libertyLibraryIterator();
}
LibertyLibrary *
find_liberty(const char *name)
{
return cmdNetwork()->findLiberty(name);
}
LibertyCell *
find_liberty_cell(const char *name)
{
return cmdNetwork()->findLibertyCell(name);
}
bool
timing_role_is_check(TimingRole *role)
{
return role->isTimingCheck();
}
%} // inline
////////////////////////////////////////////////////////////////
//
// Object Methods
//
////////////////////////////////////////////////////////////////
%extend LibertyLibrary {
const char *name() { return self->name(); }
LibertyCell *
find_liberty_cell(const char *name)
{
return self->findLibertyCell(name);
}
LibertyCellSeq
find_liberty_cells_matching(const char *pattern,
bool regexp,
bool nocase)
{
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
return self->findLibertyCellsMatching(&matcher);
}
Wireload *
find_wireload(const char *model_name)
{
return self->findWireload(model_name);
}
WireloadSelection *
find_wireload_selection(const char *selection_name)
{
return self->findWireloadSelection(selection_name);
}
OperatingConditions *
find_operating_conditions(const char *op_cond_name)
{
return self->findOperatingConditions(op_cond_name);
}
OperatingConditions *
default_operating_conditions()
{
return self->defaultOperatingConditions();
}
} // LibertyLibrary methods
%extend LibertyCell {
const char *name() { return self->name(); }
bool is_leaf() { return self->isLeaf(); }
bool is_buffer() { return self->isBuffer(); }
bool is_inverter() { return self->isInverter(); }
LibertyLibrary *liberty_library() { return self->libertyLibrary(); }
Cell *cell() { return reinterpret_cast<Cell*>(self); }
LibertyPort *
find_liberty_port(const char *name)
{
return self->findLibertyPort(name);
}
LibertyPortSeq
find_liberty_ports_matching(const char *pattern,
bool regexp,
bool nocase)
{
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
return self->findLibertyPortsMatching(&matcher);
}
LibertyCellPortIterator *
liberty_port_iterator() { return new LibertyCellPortIterator(self); }
const TimingArcSetSeq &
timing_arc_sets()
{
return self->timingArcSets();
}
void
ensure_voltage_waveforms()
{
Corner *corner = Sta::sta()->cmdCorner();
DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(MinMax::max());
self->ensureVoltageWaveforms(dcalc_ap);
}
} // LibertyCell methods
%extend LibertyPort {
const char *bus_name() { return self->busName(); }
Cell *cell() { return self->cell(); }
bool is_bus() { return self->isBus(); }
LibertyPortMemberIterator *
member_iterator() { return new LibertyPortMemberIterator(self); }
const char *
function()
{
FuncExpr *func = self->function();
if (func)
return func->asString();
else
return nullptr;
}
const char *
tristate_enable()
{
FuncExpr *enable = self->tristateEnable();
if (enable)
return enable->asString();
else
return nullptr;
}
float
capacitance(Corner *corner,
const MinMax *min_max)
{
Sta *sta = Sta::sta();
return sta->capacitance(self, corner, min_max);
}
void
set_direction(const char *dir)
{
self->setDirection(PortDirection::find(dir));
}
} // LibertyPort methods
%extend TimingArcSet {
LibertyPort *from() { return self->from(); }
LibertyPort *to() { return self->to(); }
TimingRole *role() { return self->role(); }
const char *sdf_cond() { return self->sdfCond(); }
const char *
full_name()
{
const char *from = self->from()->name();
const char *to = self->to()->name();
const char *cell_name = self->libertyCell()->name();
return stringPrintTmp("%s %s -> %s",
cell_name,
from,
to);
}
TimingArcSeq &
timing_arcs() { return self->arcs(); }
} // TimingArcSet methods
%extend TimingArc {
LibertyPort *from() { return self->from(); }
LibertyPort *to() { return self->to(); }
Transition *from_edge() { return self->fromEdge(); }
const char *from_edge_name() { return self->fromEdge()->asRiseFall()->name(); }
Transition *to_edge() { return self->toEdge(); }
const char *to_edge_name() { return self->toEdge()->asRiseFall()->name(); }
TimingRole *role() { return self->role(); }
float
time_voltage(float in_slew,
float load_cap,
float time)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms)
return waveforms->timeVoltage(in_slew, load_cap, time);
}
return 0.0;
}
float
time_current(float in_slew,
float load_cap,
float time)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms)
return waveforms->timeCurrent(in_slew, load_cap, time);
}
return 0.0;
}
float
voltage_current(float in_slew,
float load_cap,
float voltage)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms)
return waveforms->voltageCurrent(in_slew, load_cap, voltage);
}
return 0.0;
}
float
voltage_time(float in_slew,
float load_cap,
float voltage)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms)
return waveforms->voltageTime(in_slew, load_cap, voltage);
}
return 0.0;
}
Table1
voltage_waveform(float in_slew,
float load_cap)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms) {
Table1 waveform = waveforms->voltageWaveform(in_slew, load_cap);
return waveform;
}
}
return Table1();
}
const Table1 *
voltage_waveform_raw(float in_slew,
float load_cap)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms) {
const Table1 *waveform = waveforms->voltageWaveformRaw(in_slew, load_cap);
return waveform;
}
}
return nullptr;
}
Table1
current_waveform(float in_slew,
float load_cap)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms) {
Table1 waveform = waveforms->currentWaveform(in_slew, load_cap);
return waveform;
}
}
return Table1();
}
const Table1 *
current_waveform_raw(float in_slew,
float load_cap)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms) {
const Table1 *waveform = waveforms->currentWaveformRaw(in_slew, load_cap);
return waveform;
}
}
return nullptr;
}
Table1
voltage_current_waveform(float in_slew,
float load_cap)
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms) {
Table1 waveform = waveforms->voltageCurrentWaveform(in_slew, load_cap);
return waveform;
}
}
return Table1();
}
float
final_resistance()
{
GateTableModel *gate_model = self->gateTableModel();
if (gate_model) {
OutputWaveforms *waveforms = gate_model->outputWaveforms();
if (waveforms) {
return waveforms->finalResistance();
}
}
return 0.0;
}
} // TimingArc methods
%extend OperatingConditions {
float process() { return self->process(); }
float voltage() { return self->voltage(); }
float temperature() { return self->temperature(); }
}
%extend LibertyLibraryIterator {
bool has_next() { return self->hasNext(); }
LibertyLibrary *next() { return self->next(); }
void finish() { delete self; }
} // LibertyLibraryIterator methods
%extend LibertyCellPortIterator {
bool has_next() { return self->hasNext(); }
LibertyPort *next() { return self->next(); }
void finish() { delete self; }
} // LibertyCellPortIterator methods
%extend LibertyPortMemberIterator {
bool has_next() { return self->hasNext(); }
LibertyPort *next() { return self->next(); }
void finish() { delete self; }
} // LibertyPortMemberIterator methods

View File

@ -695,22 +695,22 @@ LibertyReader::endLibraryAttrs(LibertyGroup *group)
}
bool missing_threshold = false;
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_);
}
}
}

View File

@ -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");
}

View File

@ -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]);
}
////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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;

View File

@ -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

818
network/Network.i Normal file
View File

@ -0,0 +1,818 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
%module network
%{
#include "Network.hh"
%}
////////////////////////////////////////////////////////////////
//
// Empty class definitions to make swig happy.
// Private constructor/destructor so swig doesn't emit them.
//
////////////////////////////////////////////////////////////////
class Library
{
private:
Library();
~Library();
};
class LibraryIterator
{
private:
LibraryIterator();
~LibraryIterator();
};
class Cell
{
private:
Cell();
~Cell();
};
class CellPortIterator
{
private:
CellPortIterator();
~CellPortIterator();
};
class Port
{
private:
Port();
~Port();
};
class PortMemberIterator
{
private:
PortMemberIterator();
~PortMemberIterator();
};
class Instance
{
private:
Instance();
~Instance();
};
class Pin
{
private:
Pin();
~Pin();
};
class Term
{
private:
Term();
~Term();
};
class InstanceChildIterator
{
private:
InstanceChildIterator();
~InstanceChildIterator();
};
class InstancePinIterator
{
private:
InstancePinIterator();
~InstancePinIterator();
};
class InstanceNetIterator
{
private:
InstanceNetIterator();
~InstanceNetIterator();
};
class LeafInstanceIterator
{
private:
LeafInstanceIterator();
~LeafInstanceIterator();
};
class Net
{
private:
Net();
~Net();
};
class NetPinIterator
{
private:
NetPinIterator();
~NetPinIterator();
};
class NetTermIterator
{
private:
NetTermIterator();
~NetTermIterator();
};
class NetConnectedPinIterator
{
private:
NetConnectedPinIterator();
~NetConnectedPinIterator();
};
class PinConnectedPinIterator
{
private:
PinConnectedPinIterator();
~PinConnectedPinIterator();
};
%inline %{
bool
network_is_linked()
{
return Sta::sta()->cmdNetwork()->isLinked();
}
void
set_path_divider(char divider)
{
cmdNetwork()->setPathDivider(divider);
}
void
set_current_instance(Instance *inst)
{
Sta::sta()->setCurrentInstance(inst);
}
// Includes top instance.
int
network_instance_count()
{
Network *network = cmdNetwork();
return network->instanceCount();
}
int
network_pin_count()
{
Network *network = cmdNetwork();
return network->pinCount();
}
int
network_net_count()
{
Network *network = cmdNetwork();
return network->netCount();
}
int
network_leaf_instance_count()
{
Network *network = cmdNetwork();
return network->leafInstanceCount();
}
int
network_leaf_pin_count()
{
Network *network = cmdNetwork();
return network->leafPinCount();
}
Library *
find_library(const char *name)
{
return cmdNetwork()->findLibrary(name);
}
LibraryIterator *
library_iterator()
{
return cmdNetwork()->libraryIterator();
}
CellSeq
find_cells_matching(const char *pattern,
bool regexp,
bool nocase)
{
Network *network = cmdNetwork();
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
CellSeq matches;
LibraryIterator *lib_iter = network->libraryIterator();
while (lib_iter->hasNext()) {
Library *lib = lib_iter->next();
CellSeq lib_matches = network->findCellsMatching(lib, &matcher);
for (Cell *match : lib_matches)
matches.push_back(match);
}
delete lib_iter;
return matches;
}
void
set_cmd_namespace_cmd(const char *namespc)
{
Sta *sta = Sta::sta();
if (stringEq(namespc, "sdc"))
sta->setCmdNamespace(CmdNamespace::sdc);
else if (stringEq(namespc, "sta"))
sta->setCmdNamespace(CmdNamespace::sta);
else
sta->report()->warn(2120, "unknown namespace");
}
bool
link_design_cmd(const char *top_cell_name,
bool make_black_boxes)
{
return Sta::sta()->linkDesign(top_cell_name, make_black_boxes);
}
Instance *
top_instance()
{
return cmdLinkedNetwork()->topInstance();
}
LeafInstanceIterator *
leaf_instance_iterator()
{
return cmdLinkedNetwork()->leafInstanceIterator();
}
const char *
port_direction(const Port *port)
{
return cmdLinkedNetwork()->direction(port)->name();
}
const char *
pin_direction(const Pin *pin)
{
return cmdLinkedNetwork()->direction(pin)->name();
}
PortSeq
find_ports_matching(const char *pattern,
bool regexp,
bool nocase)
{
Network *network = cmdLinkedNetwork();
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
Cell *top_cell = network->cell(network->topInstance());
PortSeq matches1 = network->findPortsMatching(top_cell, &matcher);
// Expand bus/bundle ports.
PortSeq matches;
for (const Port *port : matches1) {
if (network->isBus(port)
|| network->isBundle(port)) {
PortMemberIterator *member_iter = network->memberIterator(port);
while (member_iter->hasNext()) {
Port *member = member_iter->next();
matches.push_back(member);
}
delete member_iter;
}
else
matches.push_back(port);
}
return matches;
}
PinSeq
find_port_pins_matching(const char *pattern,
bool regexp,
bool nocase)
{
Network *network = cmdLinkedNetwork();
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
Instance *top_inst = network->topInstance();
Cell *top_cell = network->cell(top_inst);
PortSeq ports = network->findPortsMatching(top_cell, &matcher);
PinSeq pins;
for (const Port *port : ports) {
if (network->isBus(port)
|| network->isBundle(port)) {
PortMemberIterator *member_iter = network->memberIterator(port);
while (member_iter->hasNext()) {
Port *member = member_iter->next();
Pin *pin = network->findPin(top_inst, member);
if (pin)
pins.push_back(pin);
}
delete member_iter;
}
else {
Pin *pin = network->findPin(top_inst, port);
if (pin)
pins.push_back(pin);
}
}
return pins;
}
Pin *
find_pin(const char *path_name)
{
return cmdLinkedNetwork()->findPin(path_name);
}
Pin *
get_port_pin(const Port *port)
{
Network *network = cmdLinkedNetwork();
return network->findPin(network->topInstance(), port);
}
PinSeq
find_pins_matching(const char *pattern,
bool regexp,
bool nocase)
{
Sta *sta = Sta::sta();
Network *network = cmdLinkedNetwork();
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
Instance *current_instance = sta->currentInstance();
PinSeq matches = network->findPinsMatching(current_instance, &matcher);
return matches;
}
PinSeq
find_pins_hier_matching(const char *pattern,
bool regexp,
bool nocase)
{
Sta *sta = Sta::sta();
Network *network = cmdLinkedNetwork();
Instance *current_instance = sta->currentInstance();
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
PinSeq matches = network->findPinsHierMatching(current_instance, &matcher);
return matches;
}
Instance *
find_instance(char *path_name)
{
return cmdLinkedNetwork()->findInstance(path_name);
}
InstanceSeq
network_leaf_instances()
{
return cmdLinkedNetwork()->leafInstances();
}
InstanceSeq
find_instances_matching(const char *pattern,
bool regexp,
bool nocase)
{
Sta *sta = Sta::sta();
Instance *current_instance = sta->currentInstance();
PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp());
Network *network = cmdLinkedNetwork();
InstanceSeq matches = network->findInstancesMatching(current_instance, &matcher);
return matches;
}
InstanceSeq
find_instances_hier_matching(const char *pattern,
bool regexp,
bool nocase)
{
Sta *sta = Sta::sta();
Network *network = cmdLinkedNetwork();
Instance *current_instance = sta->currentInstance();
PatternMatch matcher(pattern, regexp, nocase, sta->tclInterp());
InstanceSeq matches = network->findInstancesHierMatching(current_instance, &matcher);
return matches;
}
InstanceSet
find_register_instances(ClockSet *clks,
const RiseFallBoth *clk_tr,
bool edge_triggered,
bool latches)
{
cmdLinkedNetwork();
InstanceSet insts = Sta::sta()->findRegisterInstances(clks, clk_tr,
edge_triggered,
latches);
delete clks;
return insts;
}
PinSet
find_register_data_pins(ClockSet *clks,
const RiseFallBoth *clk_tr,
bool edge_triggered,
bool latches)
{
cmdLinkedNetwork();
PinSet pins = Sta::sta()->findRegisterDataPins(clks, clk_tr,
edge_triggered, latches);
delete clks;
return pins;
}
PinSet
find_register_clk_pins(ClockSet *clks,
const RiseFallBoth *clk_tr,
bool edge_triggered,
bool latches)
{
cmdLinkedNetwork();
PinSet pins = Sta::sta()->findRegisterClkPins(clks, clk_tr,
edge_triggered, latches);
delete clks;
return pins;
}
PinSet
find_register_async_pins(ClockSet *clks,
const RiseFallBoth *clk_tr,
bool edge_triggered,
bool latches)
{
cmdLinkedNetwork();
PinSet pins = Sta::sta()->findRegisterAsyncPins(clks, clk_tr,
edge_triggered, latches);
delete clks;
return pins;
}
PinSet
find_register_output_pins(ClockSet *clks,
const RiseFallBoth *clk_tr,
bool edge_triggered,
bool latches)
{
cmdLinkedNetwork();
PinSet pins = Sta::sta()->findRegisterOutputPins(clks, clk_tr,
edge_triggered, latches);
delete clks;
return pins;
}
Net *
find_net(char *path_name)
{
return cmdLinkedNetwork()->findNet(path_name);
}
NetSeq
find_nets_matching(const char *pattern,
bool regexp,
bool nocase)
{
Network *network = cmdLinkedNetwork();
Instance *current_instance = Sta::sta()->currentInstance();
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
NetSeq matches = network->findNetsMatching(current_instance, &matcher);
return matches;
}
NetSeq
find_nets_hier_matching(const char *pattern,
bool regexp,
bool nocase)
{
Network *network = cmdLinkedNetwork();
Instance *current_instance = Sta::sta()->currentInstance();
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
NetSeq matches = network->findNetsHierMatching(current_instance, &matcher);
return matches;
}
PinSet
net_driver_pins(Net *net)
{
Network *network = cmdLinkedNetwork();
PinSet pins(network);
NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
if (network->isDriver(pin))
pins.insert(pin);
}
delete pin_iter;
return pins;
}
PinSet
net_load_pins(Net *net)
{
Network *network = cmdLinkedNetwork();
PinSet pins(network);
NetConnectedPinIterator *pin_iter = network->connectedPinIterator(net);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
if (network->isLoad(pin))
pins.insert(pin);
}
delete pin_iter;
return pins;
}
const char *
pin_location(const Pin *pin)
{
Network *network = cmdNetwork();
double x, y;
bool exists;
network->location(pin, x, y, exists);
// return x/y as tcl list
if (exists)
return sta::stringPrintTmp("%f %f", x, y);
else
return "";
}
const char *
port_location(const Port *port)
{
Network *network = cmdNetwork();
const Pin *pin = network->findPin(network->topInstance(), port);
return pin_location(pin);
}
%} // inline
////////////////////////////////////////////////////////////////
//
// Object Methods
//
////////////////////////////////////////////////////////////////
%extend Library {
const char *name()
{
return cmdNetwork()->name(self);
}
Cell *
find_cell(const char *name)
{
return cmdNetwork()->findCell(self, name);
}
CellSeq
find_cells_matching(const char *pattern,
bool regexp,
bool nocase)
{
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
CellSeq matches = cmdNetwork()->findCellsMatching(self, &matcher);
return matches;
}
} // Library methods
%extend LibraryIterator {
bool has_next() { return self->hasNext(); }
Library *next() { return self->next(); }
void finish() { delete self; }
} // LibraryIterator methods
%extend Cell {
const char *name() { return cmdNetwork()->name(self); }
Library *library() { return cmdNetwork()->library(self); }
LibertyCell *liberty_cell() { return cmdNetwork()->libertyCell(self); }
bool is_leaf() { return cmdNetwork()->isLeaf(self); }
CellPortIterator *
port_iterator() { return cmdNetwork()->portIterator(self); }
string get_attribute(const char *key) { return cmdNetwork()->getAttribute(self, key); }
Port *
find_port(const char *name)
{
return cmdNetwork()->findPort(self, name);
}
PortSeq
find_ports_matching(const char *pattern,
bool regexp,
bool nocase)
{
PatternMatch matcher(pattern, regexp, nocase, Sta::sta()->tclInterp());
return cmdNetwork()->findPortsMatching(self, &matcher);
}
} // Cell methods
%extend CellPortIterator {
bool has_next() { return self->hasNext(); }
Port *next() { return self->next(); }
void finish() { delete self; }
} // CellPortIterator methods
%extend Port {
const char *bus_name() { return cmdNetwork()->busName(self); }
Cell *cell() { return cmdNetwork()->cell(self); }
LibertyPort *liberty_port() { return cmdNetwork()->libertyPort(self); }
bool is_bus() { return cmdNetwork()->isBus(self); }
PortMemberIterator *
member_iterator() { return cmdNetwork()->memberIterator(self); }
} // Port methods
%extend PortMemberIterator {
bool has_next() { return self->hasNext(); }
Port *next() { return self->next(); }
void finish() { delete self; }
} // PortMemberIterator methods
%extend Instance {
Instance *parent() { return cmdLinkedNetwork()->parent(self); }
Cell *cell() { return cmdLinkedNetwork()->cell(self); }
LibertyCell *liberty_cell() { return cmdLinkedNetwork()->libertyCell(self); }
bool is_leaf() { return cmdLinkedNetwork()->isLeaf(self); }
InstanceChildIterator *
child_iterator() { return cmdLinkedNetwork()->childIterator(self); }
InstancePinIterator *
pin_iterator() { return cmdLinkedNetwork()->pinIterator(self); }
InstanceNetIterator *
net_iterator() { return cmdLinkedNetwork()->netIterator(self); }
Pin *
find_pin(const char *name)
{
return cmdLinkedNetwork()->findPin(self, name);
}
string get_attribute(const char *key) { return cmdNetwork()->getAttribute(self, key); }
} // Instance methods
%extend InstanceChildIterator {
bool has_next() { return self->hasNext(); }
Instance *next() { return self->next(); }
void finish() { delete self; }
} // InstanceChildIterator methods
%extend LeafInstanceIterator {
bool has_next() { return self->hasNext(); }
Instance *next() { return self->next(); }
void finish() { delete self; }
} // LeafInstanceIterator methods
%extend InstancePinIterator {
bool has_next() { return self->hasNext(); }
Pin *next() { return self->next(); }
void finish() { delete self; }
} // InstancePinIterator methods
%extend InstanceNetIterator {
bool has_next() { return self->hasNext(); }
Net *next() { return self->next(); }
void finish() { delete self; }
} // InstanceNetIterator methods
%extend Pin {
const char *port_name() { return cmdLinkedNetwork()->portName(self); }
Instance *instance() { return cmdLinkedNetwork()->instance(self); }
Net *net() { return cmdLinkedNetwork()->net(self); }
Port *port() { return cmdLinkedNetwork()->port(self); }
Term *term() { return cmdLinkedNetwork()->term(self); }
LibertyPort *liberty_port() { return cmdLinkedNetwork()->libertyPort(self); }
bool is_driver() { return cmdLinkedNetwork()->isDriver(self); }
bool is_load() { return cmdLinkedNetwork()->isLoad(self); }
bool is_leaf() { return cmdLinkedNetwork()->isLeaf(self); }
bool is_hierarchical() { return cmdLinkedNetwork()->isHierarchical(self); }
bool is_top_level_port() { return cmdLinkedNetwork()->isTopLevelPort(self); }
PinConnectedPinIterator *connected_pin_iterator()
{ return cmdLinkedNetwork()->connectedPinIterator(self); }
Vertex **
vertices()
{
Vertex *vertex, *vertex_bidirect_drvr;
static Vertex *vertices[3];
cmdGraph()->pinVertices(self, vertex, vertex_bidirect_drvr);
vertices[0] = vertex;
vertices[1] = vertex_bidirect_drvr;
vertices[2] = nullptr;
return vertices;
}
} // Pin methods
%extend PinConnectedPinIterator {
bool has_next() { return self->hasNext(); }
const Pin *next() { return self->next(); }
void finish() { delete self; }
} // PinConnectedPinIterator methods
%extend Term {
Net *net() { return cmdLinkedNetwork()->net(self); }
Pin *pin() { return cmdLinkedNetwork()->pin(self); }
} // Term methods
%extend Net {
Instance *instance() { return cmdLinkedNetwork()->instance(self); }
const Net *highest_connected_net()
{ return cmdLinkedNetwork()->highestConnectedNet(self); }
NetPinIterator *pin_iterator() { return cmdLinkedNetwork()->pinIterator(self);}
NetTermIterator *term_iterator() {return cmdLinkedNetwork()->termIterator(self);}
NetConnectedPinIterator *connected_pin_iterator()
{ return cmdLinkedNetwork()->connectedPinIterator(self); }
bool is_power() { return cmdLinkedNetwork()->isPower(self);}
bool is_ground() { return cmdLinkedNetwork()->isGround(self);}
float
capacitance(Corner *corner,
const MinMax *min_max)
{
cmdLinkedNetwork();
float pin_cap, wire_cap;
Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap);
return pin_cap + wire_cap;
}
float
pin_capacitance(Corner *corner,
const MinMax *min_max)
{
cmdLinkedNetwork();
float pin_cap, wire_cap;
Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap);
return pin_cap;
}
float
wire_capacitance(Corner *corner,
const MinMax *min_max)
{
cmdLinkedNetwork();
float pin_cap, wire_cap;
Sta::sta()->connectedCap(self, corner, min_max, pin_cap, wire_cap);
return wire_cap;
}
// get_ports -of_objects net
PortSeq
ports()
{
Network *network = cmdLinkedNetwork();
PortSeq ports;
if (network->isTopInstance(network->instance(self))) {
NetTermIterator *term_iter = network->termIterator(self);
while (term_iter->hasNext()) {
Term *term = term_iter->next();
Port *port = network->port(network->pin(term));
ports.push_back(port);
}
delete term_iter;
}
return ports;
}
} // Net methods
%extend NetPinIterator {
bool has_next() { return self->hasNext(); }
const Pin *next() { return self->next(); }
void finish() { delete self; }
} // NetPinIterator methods
%extend NetTermIterator {
bool has_next() { return self->hasNext(); }
const Term *next() { return self->next(); }
void finish() { delete self; }
} // NetTermIterator methods
%extend NetConnectedPinIterator {
bool has_next() { return self->hasNext(); }
const Pin *next() { return self->next(); }
void finish() { delete self; }
} // NetConnectedPinIterator methods

View File

@ -26,9 +26,11 @@ proc_redirect report_instance {
check_argc_eq1 "report_instance" $args
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."
}

View File

@ -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;
}

View File

@ -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}

View File

@ -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
{

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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."
}

View File

@ -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);
}

View File

@ -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_));

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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());
}

View File

@ -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

View File

@ -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);
}
}

1479
sdc/Sdc.i Normal file

File diff suppressed because it is too large Load Diff

View File

@ -296,50 +296,21 @@ proc all_clocks { } {
################################################################
define_cmd_args "all_inputs" {}
define_cmd_args "all_inputs" {[-no_clocks]}
proc all_inputs { } {
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 {

View File

@ -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);

View File

@ -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"

View File

@ -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);

View File

@ -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,

View File

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

View File

@ -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();
}

View File

@ -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);

View File

@ -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;

View File

@ -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.

View File

@ -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