diff --git a/CMakeLists.txt b/CMakeLists.txt
index eaa3c353..5fe12ab8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,6 +31,7 @@ project(STA VERSION 2.5.0
option(USE_CUDD "Use CUDD BDD package")
option(CUDD_DIR "CUDD BDD package directory")
+# Turn on to debug compiler args.
set(CMAKE_VERBOSE_MAKEFILE OFF)
set(STA_HOME ${PROJECT_SOURCE_DIR})
@@ -65,6 +66,7 @@ set(STA_SOURCE
dcalc/ArcDcalcWaveforms.cc
dcalc/ArnoldiDelayCalc.cc
dcalc/ArnoldiReduce.cc
+ dcalc/CcsCeffDelayCalc.cc
dcalc/DcalcAnalysisPt.cc
dcalc/DelayCalc.cc
dcalc/DelayCalcBase.cc
@@ -145,6 +147,7 @@ set(STA_SOURCE
sdf/SdfReader.cc
sdf/SdfWriter.cc
+ search/Bdd.cc
search/Bfs.cc
search/CheckMaxSkews.cc
search/CheckMinPeriods.cc
@@ -154,6 +157,7 @@ set(STA_SOURCE
search/CheckSlewLimits.cc
search/CheckTiming.cc
search/ClkInfo.cc
+ search/ClkLatency.cc
search/ClkNetwork.cc
search/ClkSkew.cc
search/Corner.cc
@@ -188,6 +192,7 @@ set(STA_SOURCE
search/VisitPathGroupVertices.cc
search/WorstSlack.cc
search/WritePathSpice.cc
+ search/WriteSpice.cc
power/Power.cc
power/ReadVcdActivities.cc
@@ -300,77 +305,6 @@ bison_target(SdfParser ${STA_HOME}/sdf/SdfParse.yy ${CMAKE_CURRENT_BINARY_DIR}/S
add_flex_bison_dependency(SdfLex SdfParser)
-################################################################
-
-find_package(SWIG 3.0 REQUIRED)
-include(UseSWIG)
-
-set(STA_SWIG_FILE app/StaApp.i)
-
-set_property(SOURCE ${STA_SWIG_FILE}
- PROPERTY CPLUSPLUS ON
-)
-# Ubuntu 18.04 cmake version 3.10.2 does not support the
-# COMPILE_OPTIONS and INCLUDE_DIRECTORIES properties, so cram
-# them into SWIG_FLAGS for the time being.
-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
-)
-
-set(SWIG_FILES
- ${STA_HOME}/dcalc/DelayCalc.i
- ${STA_HOME}/parasitics/Parasitics.i
- ${STA_HOME}/power/Power.i
- ${STA_HOME}/sdf/Sdf.i
- ${STA_HOME}/tcl/Exception.i
- ${STA_HOME}/tcl/StaTcl.i
- ${STA_HOME}/tcl/StaTclTypes.i
- ${STA_HOME}/tcl/NetworkEdit.i
- ${STA_HOME}/verilog/Verilog.i
- )
-
-set_property(SOURCE ${STA_SWIG_FILE}
- PROPERTY DEPENDS ${SWIG_FILES}
-)
-
-swig_add_library(sta_swig
- LANGUAGE tcl
- TYPE STATIC
- SOURCES ${STA_SWIG_FILE}
-)
-
-get_target_property(STA_SWIG_CXX_FILE sta_swig SOURCES)
-
-set_source_files_properties(${STA_SWIG_CXX_FILE}
- PROPERTIES
- # 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"
- )
-
-target_link_libraries(sta_swig
- PUBLIC
- OpenSTA
-)
-
-# result build/CMakeFiles/sta_swig.dir/StaAppTCL_wrap.cxx
-target_include_directories(sta_swig
- PUBLIC
- include/sta
-
- PRIVATE
- ${STA_HOME}
- ${TCL_INCLUDE_PATH}
-)
-
################################################################
set(STA_TCL_INIT ${CMAKE_CURRENT_BINARY_DIR}/StaTclInitVar.cc)
@@ -469,6 +403,80 @@ configure_file(${STA_HOME}/util/StaConfig.hh.cmake
${STA_HOME}/include/sta/StaConfig.hh
)
+###########################################################
+# Swig
+###########################################################
+
+find_package(SWIG 3.0 REQUIRED)
+include(UseSWIG)
+
+set(STA_SWIG_FILE app/StaApp.i)
+
+set_property(SOURCE ${STA_SWIG_FILE}
+ PROPERTY CPLUSPLUS ON
+)
+# Ubuntu 18.04 cmake version 3.10.2 does not support the
+# COMPILE_OPTIONS and INCLUDE_DIRECTORIES properties, so cram
+# them into SWIG_FLAGS for the time being.
+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
+)
+
+set(SWIG_FILES
+ ${STA_HOME}/dcalc/DelayCalc.i
+ ${STA_HOME}/parasitics/Parasitics.i
+ ${STA_HOME}/power/Power.i
+ ${STA_HOME}/sdf/Sdf.i
+ ${STA_HOME}/tcl/Exception.i
+ ${STA_HOME}/tcl/StaTcl.i
+ ${STA_HOME}/tcl/StaTclTypes.i
+ ${STA_HOME}/tcl/NetworkEdit.i
+ ${STA_HOME}/verilog/Verilog.i
+ )
+
+set_property(SOURCE ${STA_SWIG_FILE}
+ PROPERTY DEPENDS ${SWIG_FILES}
+)
+
+swig_add_library(sta_swig
+ LANGUAGE tcl
+ TYPE STATIC
+ SOURCES ${STA_SWIG_FILE}
+)
+
+get_target_property(STA_SWIG_CXX_FILE sta_swig SOURCES)
+
+set_source_files_properties(${STA_SWIG_CXX_FILE}
+ PROPERTIES
+ # 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
+ PUBLIC
+ OpenSTA
+)
+
+# result build/CMakeFiles/sta_swig.dir/StaAppTCL_wrap.cxx
+target_include_directories(sta_swig
+ PUBLIC
+ include/sta
+
+ PRIVATE
+ ${STA_HOME}
+ ${TCL_INCLUDE_PATH}
+)
+
###########################################################
# Library
###########################################################
diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc
index 4b5a3cb7..bb7ea5c1 100644
--- a/dcalc/ArcDelayCalc.cc
+++ b/dcalc/ArcDelayCalc.cc
@@ -16,6 +16,10 @@
#include "ArcDelayCalc.hh"
+#include "Liberty.hh"
+#include "TimingArc.hh"
+#include "Network.hh"
+
namespace sta {
ArcDelayCalc::ArcDelayCalc(StaState *sta):
@@ -53,19 +57,85 @@ ArcDcalcArg::ArcDcalcArg() :
{
}
-ArcDcalcArg::ArcDcalcArg(const Pin *drvr_pin,
+ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
+ const Pin *drvr_pin,
Edge *edge,
const TimingArc *arc,
const Slew in_slew,
const Parasitic *parasitic) :
+ in_pin_(in_pin),
drvr_pin_(drvr_pin),
edge_(edge),
arc_(arc),
in_slew_(in_slew),
- parasitic_(parasitic)
+ parasitic_(parasitic),
+ input_delay_(0.0)
{
}
+ArcDcalcArg::ArcDcalcArg(const Pin *in_pin,
+ const Pin *drvr_pin,
+ Edge *edge,
+ const TimingArc *arc,
+ float input_delay) :
+ in_pin_(in_pin),
+ drvr_pin_(drvr_pin),
+ edge_(edge),
+ arc_(arc),
+ in_slew_(0.0),
+ parasitic_(nullptr),
+ input_delay_(input_delay)
+{
+}
+
+ArcDcalcArg::ArcDcalcArg(const ArcDcalcArg &arg) :
+ in_pin_(arg.in_pin_),
+ drvr_pin_(arg.drvr_pin_),
+ edge_(arg.edge_),
+ arc_(arg.arc_),
+ in_slew_(arg.in_slew_),
+ parasitic_(arg.parasitic_),
+ input_delay_(arg.input_delay_)
+{
+}
+
+const RiseFall *
+ArcDcalcArg::inEdge() const
+{
+ return arc_->fromEdge()->asRiseFall();
+}
+
+LibertyCell *
+ArcDcalcArg::drvrCell() const
+{
+
+ return arc_->to()->libertyCell();
+}
+
+const LibertyLibrary *
+ArcDcalcArg::drvrLibrary() const
+{
+ return arc_->to()->libertyLibrary();
+}
+
+const RiseFall *
+ArcDcalcArg::drvrEdge() const
+{
+ return arc_->toEdge()->asRiseFall();
+}
+
+const Net *
+ArcDcalcArg::drvrNet(const Network *network) const
+{
+ return network->net(drvr_pin_);
+}
+
+void
+ArcDcalcArg::setInSlew(Slew in_slew)
+{
+ in_slew_ = in_slew;
+}
+
void
ArcDcalcArg::setParasitic(const Parasitic *parasitic)
{
diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc
index 5c80c382..91a36412 100644
--- a/dcalc/ArnoldiReduce.cc
+++ b/dcalc/ArnoldiReduce.cc
@@ -314,7 +314,8 @@ ArnoldiReduce::findPt(ParasiticNode *node)
rcmodel *
ArnoldiReduce::makeRcmodelDrv()
{
- ParasiticNode *drv_node = parasitics_->findNode(parasitic_network_, drvr_pin_);
+ ParasiticNode *drv_node =
+ parasitics_->findParasiticNode(parasitic_network_, drvr_pin_);
ts_point *pdrv = findPt(drv_node);
makeRcmodelDfs(pdrv);
getRC();
diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc
new file mode 100644
index 00000000..7c11eec7
--- /dev/null
+++ b/dcalc/CcsCeffDelayCalc.cc
@@ -0,0 +1,678 @@
+// OpenSTA, Static Timing Analyzer
+// Copyright (c) 2024, Parallax Software, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "CcsCeffDelayCalc.hh"
+
+#include "Debug.hh"
+#include "Units.hh"
+#include "Liberty.hh"
+#include "TimingArc.hh"
+#include "Network.hh"
+#include "Graph.hh"
+#include "Corner.hh"
+#include "DcalcAnalysisPt.hh"
+#include "Parasitics.hh"
+#include "GraphDelayCalc.hh"
+#include "DmpDelayCalc.hh"
+#include "FindRoot.hh"
+
+namespace sta {
+
+// Implementaion based on:
+// "Gate Delay Estimation with Library Compatible Current Source Models
+// and Effective Capacitance", D. Garyfallou et al,
+// IEEE Transactions on Very Large Scale Integration (VLSI) Systems, March 2021
+
+using std::abs;
+using std::exp;
+using std::make_shared;
+
+ArcDelayCalc *
+makeCcsCeffDelayCalc(StaState *sta)
+{
+ return new CcsCeffDelayCalc(sta);
+}
+
+CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
+ LumpedCapDelayCalc(sta),
+ output_waveforms_(nullptr),
+ // Includes the Vh:Vdd region.
+ region_count_(0),
+ vl_fail_(false),
+ capacitance_unit_(units_->capacitanceUnit()),
+ table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
+{
+}
+
+CcsCeffDelayCalc::~CcsCeffDelayCalc()
+{
+ delete table_dcalc_;
+}
+
+ArcDelayCalc *
+CcsCeffDelayCalc::copy()
+{
+ return new CcsCeffDelayCalc(this);
+}
+
+ArcDcalcResult
+CcsCeffDelayCalc::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)
+{
+ bool vdd_exists;
+ LibertyCell *drvr_cell = arc->to()->libertyCell();
+ const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
+ const RiseFall *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_;
+ in_slew_ = in_slew;
+ load_cap_ = load_cap;
+ parasitic_ = parasitic;
+ drvr_cell->ensureVoltageWaveforms();
+ output_waveforms_ = nullptr;
+
+ GateTableModel *table_model = gateTableModel(arc, dcalc_ap);
+ if (table_model && parasitic) {
+ OutputWaveforms *output_waveforms = table_model->outputWaveforms();
+ parasitics_->piModel(parasitic, c2_, rpi_, c1_);
+ if (output_waveforms
+ && rpi_ > 0.0 && c1_ > 0.0
+ // Bounds check because extrapolating waveforms does not work for shit.
+ && output_waveforms->slewAxis()->inBounds(in_slew_)
+ && output_waveforms->capAxis()->inBounds(c2_)
+ && output_waveforms->capAxis()->inBounds(load_cap_)) {
+ 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());
+ 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);
+ }
+ }
+ return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
+ load_pin_index_map, dcalc_ap);
+}
+
+void
+CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
+ const RiseFall *rf,
+ // Return values.
+ ArcDelay &gate_delay,
+ Slew &drvr_slew)
+{
+ initRegions(drvr_library, rf);
+ findCsmWaveform();
+ ref_time_ = output_waveforms_->referenceTime(in_slew_);
+ gate_delay = region_times_[region_vth_idx_] - ref_time_;
+ drvr_slew = abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
+ debugPrint(debug_, "ccs_dcalc", 2,
+ "gate_delay %s drvr_slew %s (initial)",
+ delayAsString(gate_delay, this),
+ delayAsString(drvr_slew, this));
+ float prev_drvr_slew = delayAsFloat(drvr_slew);
+ constexpr int max_iterations = 5;
+ for (int iter = 0; iter < max_iterations; iter++) {
+ debugPrint(debug_, "ccs_dcalc", 2, "iteration %d", iter);
+ // Init drvr ramp model for vl.
+ for (size_t i = 0; i <= region_count_; i++) {
+ region_ramp_times_[i] = region_times_[i];
+ if (i < region_count_)
+ region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i])
+ / (region_times_[i + 1] - region_times_[i]);
+ }
+
+ for (size_t i = 0; i < region_count_; i++) {
+ double v1 = region_volts_[i];
+ double v2 = region_volts_[i + 1];
+ double t1 = region_times_[i];
+ double t2 = region_times_[i + 1];
+
+ // Receiver cap Cp(l) assumed constant and included in c1.
+ // Note that eqn 8 in the ref'd paper does not properly account
+ // for the charge on c1 from previous segments so it does not
+ // work well.
+ double c1_v1, c1_v2, ignore;
+ vl(t1, rpi_ * c1_, c1_v1, ignore);
+ vl(t2, rpi_ * c1_, c1_v2, ignore);
+ double q1 = v1 * c2_ + c1_v1 * c1_;
+ double q2 = v2 * c2_ + c1_v2 * c1_;
+ double ceff = (q2 - q1) / (v2 - v1);
+
+ debugPrint(debug_, "ccs_dcalc", 2, "ceff %s",
+ capacitance_unit_->asString(ceff));
+ region_ceff_[i] = ceff;
+ }
+ findCsmWaveform();
+ gate_delay = region_times_[region_vth_idx_] - ref_time_;
+ drvr_slew = abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
+ debugPrint(debug_, "ccs_dcalc", 2,
+ "gate_delay %s drvr_slew %s",
+ delayAsString(gate_delay, this),
+ delayAsString(drvr_slew, this));
+ if (abs(delayAsFloat(drvr_slew) - prev_drvr_slew) < .01 * prev_drvr_slew)
+ break;
+ prev_drvr_slew = delayAsFloat(drvr_slew);
+ }
+}
+
+void
+CcsCeffDelayCalc::initRegions(const LibertyLibrary *drvr_library,
+ const RiseFall *rf)
+{
+ // Falling waveforms are treated as rising to simplify the conditionals.
+ if (rf == RiseFall::fall()) {
+ vl_ = (1.0 - drvr_library->slewUpperThreshold(rf)) * vdd_;
+ vh_ = (1.0 - drvr_library->slewLowerThreshold(rf)) * vdd_;
+ }
+
+ // Includes the Vh:Vdd region.
+ region_count_ = 7;
+ region_volts_.resize(region_count_ + 1);
+ region_ceff_.resize(region_count_ + 1);
+ region_times_.resize(region_count_ + 1);
+ region_begin_times_.resize(region_count_ + 1);
+ region_end_times_.resize(region_count_ + 1);
+ region_time_offsets_.resize(region_count_ + 1);
+ region_ramp_times_.resize(region_count_ + 1);
+ region_ramp_slopes_.resize(region_count_ + 1);
+
+ region_vl_idx_ = 1;
+ region_vh_idx_ = region_count_ - 1;
+
+ double vth_vh = (vh_ - vth_);
+ switch (region_count_) {
+ case 4:
+ region_vth_idx_ = 2;
+ region_volts_ = {0.0, vl_, vth_, vh_, vdd_};
+ break;
+ case 5: {
+ region_vth_idx_ = 2;
+ double v1 = vth_ + .7 * vth_vh;
+ region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_};
+ break;
+ }
+ case 6: {
+ region_vth_idx_ = 2;
+ double v1 = vth_ + .3 * vth_vh;
+ double v2 = vth_ + .6 * vth_vh;
+ region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_};
+ break;
+ }
+ case 7: {
+ region_vth_idx_ = 2;
+ region_vh_idx_ = 5;
+ double v1 = vth_ + .3 * vth_vh;
+ double v2 = vth_ + .6 * vth_vh;
+ double v3 = vh_ + .5 * (vdd_ - vh_);
+ region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_};
+ break;
+ }
+ case 8: {
+ region_vth_idx_ = 2;
+ region_vh_idx_ = 6;
+ double v1 = vth_ + .25 * vth_vh;
+ double v2 = vth_ + .50 * vth_vh;
+ double v3 = vth_ + .75 * vth_vh;
+ double v4 = vh_ + .5 * (vdd_ - vh_);
+ region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_};
+ break;
+ }
+ case 9: {
+ region_vth_idx_ = 2;
+ region_vh_idx_ = 7;
+ double v1 = vth_ + .2 * vth_vh;
+ double v2 = vth_ + .4 * vth_vh;
+ double v3 = vth_ + .6 * vth_vh;
+ double v4 = vth_ + .8 * vth_vh;
+ double v5 = vh_ + .5 * (vdd_ - vh_);
+ region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_};
+ break;
+ }
+ case 10: {
+ region_vth_idx_ = 2;
+ region_vh_idx_ = 7;
+ double v1 = vth_ + .2 * vth_vh;
+ double v2 = vth_ + .4 * vth_vh;
+ double v3 = vth_ + .6 * vth_vh;
+ double v4 = vth_ + .8 * vth_vh;
+ double v5 = vh_ + .3 * (vdd_ - vh_);
+ double v6 = vh_ + .6 * (vdd_ - vh_);
+ region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_};
+ break;
+ }
+ default:
+ report_->error(1701, "unsupported ccs region count.");
+ break;
+ }
+ fill(region_ceff_.begin(), region_ceff_.end(), c2_ + c1_);
+}
+
+void
+CcsCeffDelayCalc::findCsmWaveform()
+{
+ for (size_t i = 0; i < region_count_; i++) {
+ double t1 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
+ region_volts_[i]);
+ double t2 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
+ region_volts_[i + 1]);
+ region_begin_times_[i] = t1;
+ region_end_times_[i] = t2;
+ double time_offset = (i == 0)
+ ? 0.0
+ : t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]);
+ region_time_offsets_[i] = time_offset;
+
+ if (i == 0)
+ region_times_[i] = t1 - time_offset;
+ region_times_[i + 1] = t2 - time_offset;
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+ArcDcalcResult
+CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library,
+ const RiseFall *rf,
+ ArcDelay &gate_delay,
+ Slew &drvr_slew,
+ const LoadPinIndexMap &load_pin_index_map)
+{
+ ArcDcalcResult dcalc_result(load_pin_index_map.size());
+ debugPrint(debug_, "ccs_dcalc", 2,
+ "gate_delay %s drvr_slew %s",
+ delayAsString(gate_delay, this),
+ delayAsString(drvr_slew, this));
+ 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;
+ ArcDelay wire_delay;
+ Slew load_slew;
+ loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, load_slew);
+ dcalc_result.setWireDelay(load_idx, wire_delay);
+ dcalc_result.setLoadSlew(load_idx, load_slew);
+ }
+ return dcalc_result;
+}
+
+void
+CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
+ const LibertyLibrary *drvr_library,
+ const RiseFall *rf,
+ Slew &drvr_slew,
+ // Return values.
+ ArcDelay &wire_delay,
+ Slew &load_slew)
+{
+ ArcDelay wire_delay1 = 0.0;
+ Slew load_slew1 = drvr_slew;
+ bool elmore_exists = false;
+ float elmore = 0.0;
+ if (parasitic_
+ && parasitics_->isPiElmore(parasitic_))
+ parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
+
+ if (elmore_exists &&
+ (elmore == 0.0
+ // Elmore delay is small compared to driver slew.
+ || elmore < delayAsFloat(drvr_slew) * 1e-3)) {
+ wire_delay1 = elmore;
+ load_slew1 = drvr_slew;
+ }
+ else
+ loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay1, load_slew1);
+
+ thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
+ wire_delay = wire_delay1;
+ load_slew = load_slew1;
+}
+
+void
+CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
+ Slew &drvr_slew,
+ float elmore,
+ // Return values.
+ ArcDelay &delay,
+ Slew &slew)
+{
+ for (size_t i = 0; i <= region_count_; i++) {
+ region_ramp_times_[i] = region_times_[i];
+ if (i < region_count_)
+ region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i])
+ / (region_times_[i + 1] - region_times_[i]);
+ }
+
+ vl_fail_ = false;
+ double t_vl = findVlTime(vl_, elmore);
+ double t_vth = findVlTime(vth_, elmore);
+ double t_vh = findVlTime(vh_, elmore);
+ if (!vl_fail_) {
+ delay = t_vth - region_times_[region_vth_idx_];
+ slew = t_vh - t_vl;
+ }
+ else {
+ delay = elmore;
+ slew = drvr_slew;
+ fail("load delay threshold crossing");
+ }
+ debugPrint(debug_, "ccs_dcalc", 2,
+ "load %s delay %s slew %s",
+ network_->pathName(load_pin),
+ delayAsString(delay, this),
+ delayAsString(slew, this));
+}
+
+// Elmore (one pole) response to ramp with slope slew.
+static void
+rampElmoreV(double t,
+ double slew,
+ double elmore,
+ // Return values.
+ double &v,
+ double &dv_dt)
+{
+ double exp_t = 1.0 - exp2(-t / elmore);
+ v = slew * (t - elmore * exp_t);
+ // First derivative of loadVl dv/dt.
+ dv_dt = slew * exp_t;
+}
+
+// Elmore (one pole) response to 2 segment ramps [0, vth] slew1, [vth, vdd] slew2.
+void
+CcsCeffDelayCalc::vl(double t,
+ double elmore,
+ // Return values.
+ double &vl,
+ double &dvl_dt)
+{
+ vl = 0.0;
+ dvl_dt = 0.0;
+ for (size_t i = 0; i < region_count_; i++) {
+ double t_begin = region_ramp_times_[i];
+ double t_end = region_ramp_times_[i + 1];
+ double ramp_slope = region_ramp_slopes_[i];
+ if (t >= t_begin) {
+ double v, dv_dt;
+ rampElmoreV(t - t_begin, ramp_slope, elmore, v, dv_dt);
+ vl += v;
+ dvl_dt += dv_dt;
+ }
+ if (t > t_end) {
+ double v, dv_dt;
+ rampElmoreV(t - t_end, ramp_slope, elmore, v, dv_dt);
+ vl -= v;
+ dvl_dt -= dv_dt;
+ }
+ }
+}
+
+// for debugging
+double
+CcsCeffDelayCalc::vl(double t,
+ double elmore)
+{
+ double vl1, dvl_dt;
+ vl(t, elmore, vl1, dvl_dt);
+ return vl1;
+}
+
+double
+CcsCeffDelayCalc::findVlTime(double v,
+ double elmore)
+{
+ double t_init = region_ramp_times_[0];
+ double t_final = region_ramp_times_[region_count_];
+ bool root_fail = false;
+ double time = findRoot([=] (double t,
+ double &y,
+ double &dy) {
+ vl(t, elmore, y, dy);
+ y -= v;
+ }, t_init, t_final + elmore * 3.0, .001, 20, root_fail);
+ vl_fail_ |= root_fail;
+ return time;
+}
+
+////////////////////////////////////////////////////////////////
+
+// 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)
+{
+ bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
+ drvr_rf, corner, min_max);
+ if (dcalc_success)
+ return drvrWaveform(in_slew_, drvr_rf);
+ else
+ return Table1();
+}
+
+Table1
+CcsCeffDelayCalc::drvrWaveform(const Slew &in_slew,
+ const RiseFall *drvr_rf)
+{
+ // Stitch together the ccs waveforms for each region.
+ FloatSeq *drvr_times = new FloatSeq;
+ FloatSeq *drvr_volts = new FloatSeq;
+ for (size_t i = 0; i < region_count_; i++) {
+ double t1 = region_begin_times_[i];
+ double t2 = region_end_times_[i];
+ size_t time_steps = 10;
+ double time_step = (t2 - t1) / time_steps;
+ double time_offset = region_time_offsets_[i];
+ for (size_t s = 0; s <= time_steps; s++) {
+ double t = t1 + s * time_step;
+ drvr_times->push_back(t - time_offset);
+ double v = output_waveforms_->timeVoltage(delayAsFloat(in_slew),
+ region_ceff_[i], t);
+ if (drvr_rf == RiseFall::fall())
+ v = vdd_ - v;
+ drvr_volts->push_back(v);
+ }
+ }
+ TableAxisPtr drvr_time_axis = make_shared(TableAxisVariable::time,
+ drvr_times);
+ Table1 drvr_table(drvr_volts, drvr_time_axis);
+ return drvr_table;
+}
+
+// For debugging
+Table1
+CcsCeffDelayCalc::loadWaveform(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ const Pin *load_pin,
+ const Corner *corner,
+ const MinMax *min_max)
+{
+ bool elmore_exists = false;
+ float elmore = 0.0;
+ if (parasitic_) {
+ 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) {
+ FloatSeq *load_times = new FloatSeq;
+ FloatSeq *load_volts = new FloatSeq;
+ double t_vh = findVlTime(vh_, elmore);
+ double dt = t_vh / 20.0;
+ double v_final = vh_ + (vdd_ - vh_) * .8;
+ double v = 0.0;
+ for (double t = 0; v < v_final; t += dt) {
+ load_times->push_back(t);
+
+ double ignore;
+ vl(t, elmore, v, ignore);
+ double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
+ load_volts->push_back(v1);
+ }
+ TableAxisPtr load_time_axis = make_shared(TableAxisVariable::time,
+ load_times);
+ Table1 load_table(load_volts, load_time_axis);
+ return load_table;
+ }
+ }
+ return Table1();
+}
+
+Table1
+CcsCeffDelayCalc::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)
+{
+ bool elmore_exists = false;
+ float elmore = 0.0;
+ if (parasitic_) {
+ parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
+ bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
+ drvr_rf, corner, min_max);
+ if (dcalc_success
+ && elmore_exists) {
+ FloatSeq *load_times = new FloatSeq;
+ FloatSeq *load_volts = new FloatSeq;
+ for (size_t j = 0; j <= region_count_; j++) {
+ double t = region_ramp_times_[j];
+ load_times->push_back(t);
+ double v = 0.0;
+ for (size_t i = 0; i < region_count_; i++) {
+ double t_begin = region_ramp_times_[i];
+ double t_end = region_ramp_times_[i + 1];
+ double ramp_slope = region_ramp_slopes_[i];
+ if (t >= t_begin)
+ v += (t - t_begin) * ramp_slope;
+ if (t > t_end)
+ v -= (t - t_end) * ramp_slope;
+ }
+ double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
+ load_volts->push_back(v1);
+ }
+ TableAxisPtr load_time_axis = make_shared(TableAxisVariable::time,
+ load_times);
+ Table1 load_table(load_volts, load_time_axis);
+ return load_table;
+ }
+ }
+ return Table1();
+}
+
+bool
+CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ const Corner *corner,
+ const MinMax *min_max)
+{
+ Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
+ Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
+ Edge *edge = nullptr;
+ VertexInEdgeIterator edge_iter(drvr_vertex, graph_);
+ while (edge_iter.hasNext()) {
+ edge = edge_iter.next();
+ Vertex *from_vertex = edge->from(graph_);
+ const Pin *from_pin = from_vertex->pin();
+ if (from_pin == in_pin)
+ break;
+ }
+ if (edge) {
+ TimingArc *arc = nullptr;
+ for (TimingArc *arc1 : edge->timingArcSet()->arcs()) {
+ if (arc1->fromEdge()->asRiseFall() == in_rf
+ && arc1->toEdge()->asRiseFall() == drvr_rf) {
+ arc = arc1;
+ break;
+ }
+ }
+ if (arc) {
+ DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
+ const Slew &in_slew = graph_->slew(in_vertex, in_rf, dcalc_ap->index());
+ parasitic_ = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, dcalc_ap);
+ if (parasitic_) {
+ parasitics_->piModel(parasitic_, c2_, rpi_, c1_);
+ LoadPinIndexMap load_pin_index_map =
+ graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
+ gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_,
+ load_pin_index_map, dcalc_ap);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////
+
+string
+CcsCeffDelayCalc::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)
+{
+ Parasitic *pi_elmore = nullptr;
+ const RiseFall *rf = arc->toEdge()->asRiseFall();
+ if (parasitic && !parasitics_->isPiElmore(parasitic)) {
+ const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt();
+ pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf,
+ dcalc_ap->corner(),
+ dcalc_ap->constraintMinMax(), ap);
+ }
+ string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap,
+ pi_elmore, load_pin_index_map,
+ dcalc_ap, digits);
+ parasitics_->deleteDrvrReducedParasitics(drvr_pin);
+ return report;
+}
+
+void
+CcsCeffDelayCalc::fail(const char *reason)
+{
+ // Report failures with a unique debug flag.
+ if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1))
+ report_->reportLine("delay_calc: CCS failed - %s", reason);
+}
+
+} // namespace
diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh
new file mode 100644
index 00000000..35094007
--- /dev/null
+++ b/dcalc/CcsCeffDelayCalc.hh
@@ -0,0 +1,157 @@
+// OpenSTA, Static Timing Analyzer
+// Copyright (c) 2024, Parallax Software, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#pragma once
+
+#include "LumpedCapDelayCalc.hh"
+#include "ArcDcalcWaveforms.hh"
+
+namespace sta {
+
+using std::vector;
+
+ArcDelayCalc *
+makeCcsCeffDelayCalc(StaState *sta);
+
+class CcsCeffDelayCalc : public LumpedCapDelayCalc, public ArcDcalcWaveforms
+{
+public:
+ CcsCeffDelayCalc(StaState *sta);
+ virtual ~CcsCeffDelayCalc();
+ ArcDelayCalc *copy() 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;
+ 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 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;
+
+protected:
+ typedef vector Region;
+
+ void gateDelaySlew(const LibertyLibrary *drvr_library,
+ const RiseFall *rf,
+ // Return values.
+ ArcDelay &gate_delay,
+ Slew &drvr_slew);
+ void initRegions(const LibertyLibrary *drvr_library,
+ const RiseFall *rf);
+ void findCsmWaveform();
+ ArcDcalcResult makeResult(const LibertyLibrary *drvr_library,
+ const RiseFall *rf,
+ ArcDelay &gate_delay,
+ Slew &drvr_slew,
+ const LoadPinIndexMap &load_pin_index_map);
+ void loadDelaySlew(const Pin *load_pin,
+ const LibertyLibrary *drvr_library,
+ const RiseFall *rf,
+ Slew &drvr_slew,
+ // Return values.
+ ArcDelay &wire_delay,
+ Slew &load_slew);
+ void loadDelaySlew(const Pin *load_pin,
+ Slew &drvr_slew,
+ float elmore,
+ // Return values.
+ ArcDelay &delay,
+ Slew &slew);
+ 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);
+ void vl(double t,
+ double elmore,
+ // Return values.
+ double &vl,
+ 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_;
+ double in_slew_;
+ double load_cap_;
+ const Parasitic *parasitic_;
+
+ OutputWaveforms *output_waveforms_;
+ double ref_time_;
+ float vdd_;
+ float vth_;
+ float vl_;
+ float vh_;
+
+ float c2_;
+ float rpi_;
+ float c1_;
+
+ size_t region_count_;
+ size_t region_vl_idx_;
+ size_t region_vth_idx_;
+ size_t region_vh_idx_;
+
+ Region region_volts_;
+ Region region_ceff_;
+ Region region_times_;
+ Region region_begin_times_;
+ Region region_end_times_;
+ Region region_time_offsets_;
+ Region region_ramp_times_;
+ Region region_ramp_slopes_;
+ bool vl_fail_;
+
+ const Unit *capacitance_unit_;
+ // Delay calculator to use when ccs waveforms are missing from liberty.
+ ArcDelayCalc *table_dcalc_;
+};
+
+} // namespace
diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc
index ab032824..2fe5dcd5 100644
--- a/dcalc/DelayCalc.cc
+++ b/dcalc/DelayCalc.cc
@@ -22,6 +22,7 @@
#include "LumpedCapDelayCalc.hh"
#include "DmpDelayCalc.hh"
#include "ArnoldiDelayCalc.hh"
+#include "CcsCeffDelayCalc.hh"
namespace sta {
@@ -37,6 +38,7 @@ registerDelayCalcs()
registerDelayCalc("dmp_ceff_elmore", makeDmpCeffElmoreDelayCalc);
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
+ registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc);
}
void
diff --git a/dcalc/DelayCalc.tcl b/dcalc/DelayCalc.tcl
index 7af909e5..c93684db 100644
--- a/dcalc/DelayCalc.tcl
+++ b/dcalc/DelayCalc.tcl
@@ -16,6 +16,13 @@
namespace eval sta {
+define_cmd_args "report_dcalc" \
+ {[-from from_pin] [-to to_pin] [-corner corner] [-min] [-max] [-digits digits]}
+
+proc_redirect report_dcalc {
+ report_dcalc_cmd "report_dcalc" $args "-digits"
+}
+
# Allow any combination of -from/-to pins.
proc report_dcalc_cmd { cmd cmd_args digits_key } {
global sta_report_default_digits
diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc
index da3b40d0..87ccc66d 100644
--- a/dcalc/GraphDelayCalc.cc
+++ b/dcalc/GraphDelayCalc.cc
@@ -954,14 +954,14 @@ GraphDelayCalc::makeArcDcalcArgs(Vertex *drvr_vertex,
// Shockingly one fpga vendor connects outputs with no timing arcs together.
if (edge1) {
Vertex *from_vertex = edge1->from(graph_);
+ const Pin *from_pin = from_vertex->pin();
const RiseFall *from_rf = arc1->fromEdge()->asRiseFall();
const RiseFall *drvr_rf = arc1->toEdge()->asRiseFall();
const Slew in_slew = edgeFromSlew(from_vertex, from_rf, edge1, dcalc_ap);
const Pin *drvr_pin1 = drvr_vertex1->pin();
Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin1, drvr_rf,
dcalc_ap);
- dcalc_args.push_back(ArcDcalcArg(drvr_pin1, edge1, arc1, in_slew,
- parasitic));
+ dcalc_args.emplace_back(from_pin, drvr_pin1, edge1, arc1, in_slew, parasitic);
}
}
return dcalc_args;
diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt
index 30afc942..2682fccf 100644
Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ
diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf
index b04d5b6e..d0e5f76e 100644
Binary files a/doc/OpenSTA.pdf and b/doc/OpenSTA.pdf differ
diff --git a/graph/Graph.cc b/graph/Graph.cc
index 2007e610..c24d69ca 100644
--- a/graph/Graph.cc
+++ b/graph/Graph.cc
@@ -498,6 +498,37 @@ Graph::deleteOutEdge(Vertex *vertex,
Graph::edge(next)->vertex_out_prev_ = prev;
}
+void
+Graph::gateEdgeArc(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ // Return values.
+ Edge *&edge,
+ const TimingArc *&arc) const
+{
+ Vertex *in_vertex = pinLoadVertex(in_pin);
+ Vertex *drvr_vertex = pinDrvrVertex(drvr_pin);
+ // Iterate over load drivers to avoid driver fanout^2.
+ VertexInEdgeIterator edge_iter(drvr_vertex, this);
+ while (edge_iter.hasNext()) {
+ Edge *edge1 = edge_iter.next();
+ if (edge1->from(this) == in_vertex) {
+ TimingArcSet *arc_set = edge1->timingArcSet();
+ for (TimingArc *arc1 : arc_set->arcs()) {
+ if (arc1->fromEdge()->asRiseFall() == in_rf
+ && arc1->toEdge()->asRiseFall() == drvr_rf) {
+ edge = edge1;
+ arc = arc1;
+ return;
+ }
+ }
+ }
+ }
+ edge = nullptr;
+ arc = nullptr;
+}
+
////////////////////////////////////////////////////////////////
Arrival *
diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh
index 2bcc11a8..62ce4817 100644
--- a/include/sta/ArcDelayCalc.hh
+++ b/include/sta/ArcDelayCalc.hh
@@ -50,24 +50,41 @@ class ArcDcalcArg
{
public:
ArcDcalcArg();
- ArcDcalcArg(const Pin *drvr_pin,
- Edge *edge,
- const TimingArc *arc,
- const Slew in_slew,
- const Parasitic *parasitic);
+ ArcDcalcArg(const ArcDcalcArg &arg);
+ ArcDcalcArg(const Pin *in_pin,
+ const Pin *drvr_pin,
+ Edge *edge,
+ const TimingArc *arc,
+ const Slew in_slew,
+ const Parasitic *parasitic);
+ ArcDcalcArg(const Pin *in_pin,
+ const Pin *drvr_pin,
+ Edge *edge,
+ const TimingArc *arc,
+ float in_delay);
+ const Pin *inPin() const { return in_pin_; }
+ const RiseFall *inEdge() const;
const Pin *drvrPin() const { return drvr_pin_; }
+ LibertyCell *drvrCell() const;
+ const LibertyLibrary *drvrLibrary() const;
+ const RiseFall *drvrEdge() const;
+ const Net *drvrNet(const Network *network) const;
Edge *edge() const { return edge_; }
const TimingArc *arc() const { return arc_; }
Slew inSlew() const { return in_slew_; }
+ void setInSlew(Slew in_slew);
const Parasitic *parasitic() { return parasitic_; }
void setParasitic(const Parasitic *parasitic);
+ float inputDelay() const { return input_delay_; }
protected:
+ const Pin *in_pin_;
const Pin *drvr_pin_;
Edge *edge_;
const TimingArc *arc_;
Slew in_slew_;
const Parasitic *parasitic_;
+ float input_delay_;
};
// Arc delay calc result.
@@ -163,7 +180,7 @@ public:
const DcalcAnalysisPt *dcalc_ap,
// Return values.
ArcDelay &gate_delay,
- Slew &drvr_slew) __attribute__ ((deprecated));
+ Slew &drvr_slew) __attribute__ ((deprecated));
// Find gate delays and slews for parallel gates.
virtual ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &args,
diff --git a/include/sta/Bdd.hh b/include/sta/Bdd.hh
new file mode 100644
index 00000000..bc95283d
--- /dev/null
+++ b/include/sta/Bdd.hh
@@ -0,0 +1,57 @@
+// OpenSTA, Static Timing Analyzer
+// Copyright (c) 2024, Parallax Software, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#pragma once
+
+#include