diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ab8e49f..961cc8e0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,6 +69,7 @@ set(STA_SOURCE
dcalc/ArnoldiDelayCalc.cc
dcalc/ArnoldiReduce.cc
dcalc/CcsCeffDelayCalc.cc
+ dcalc/CcsSimDelayCalc.cc
dcalc/DcalcAnalysisPt.cc
dcalc/DelayCalc.cc
dcalc/DelayCalcBase.cc
@@ -363,6 +364,8 @@ include(FindZLIB)
find_package(Threads)
+find_package(Eigen3 REQUIRED)
+
################################################################
#
# Locate CUDD bdd package.
@@ -508,6 +511,7 @@ target_sources(OpenSTA
)
target_link_libraries(OpenSTA
+ Eigen3::Eigen
${TCL_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
)
diff --git a/README.md b/README.md
index 79405a52..db16c0f0 100644
--- a/README.md
+++ b/README.md
@@ -85,26 +85,27 @@ The build dependency versions are show below. Other versions may
work, but these are the versions used for development.
```
- from Ubuntu Xcode
- 22.04.2 11.3
-cmake 3.10.2 3.24.2 3.16.2
-clang 9.1.0 14.0.3
-gcc 3.3.2 11.3.0
+ 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.0.1
-bison 1.35 3.0.2 3.8.2
+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
```
Note that flex versions before 2.6.4 contain 'register' declarations that
are illegal in c++17.
-These packages are **optional**:
-
+External library dependencies:
```
-tclreadline 2.3.8
-libz 1.1.4 1.2.5 1.2.8
-cudd 2.4.1 3.0.0
+ 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
```
The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html)
diff --git a/dcalc/ArcDelayCalc.cc b/dcalc/ArcDelayCalc.cc
index bb7ea5c1..15f5a581 100644
--- a/dcalc/ArcDelayCalc.cc
+++ b/dcalc/ArcDelayCalc.cc
@@ -130,6 +130,12 @@ ArcDcalcArg::drvrNet(const Network *network) const
return network->net(drvr_pin_);
}
+float
+ArcDcalcArg::inSlewFlt() const
+{
+ return delayAsFloat(in_slew_);
+}
+
void
ArcDcalcArg::setInSlew(Slew in_slew)
{
diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc
index 5cb2cf86..20f8d9e5 100644
--- a/dcalc/ArnoldiDelayCalc.cc
+++ b/dcalc/ArnoldiDelayCalc.cc
@@ -1334,7 +1334,6 @@ ArnoldiDelayCalc::ra_get_s(delay_work *D,
tlohi = slew_derate*delayAsFloat(s1);
smin = r*c*c_smin; // c_smin = ra_hinv((1-vhi)/vhi-log(vhi)) + log(vhi);
if (c_log*r*c >= tlohi) {
- // printf("hit smin\n");
s = smin;
} else {
s = smin+0.3*tlohi;
@@ -1410,7 +1409,6 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
if (debug_->check("arnoldi", 1)) {
double p = 1.0/(r*ctot);
double thix,tlox;
- if (bad) printf("bad\n");
debugPrint(debug_, "arnoldi", 1, "at r=%s s=%s",
units_->resistanceUnit()->asString(r),
units_->timeUnit()->asString(s));
diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc
index 7c11eec7..9e12ecf5 100644
--- a/dcalc/CcsCeffDelayCalc.cc
+++ b/dcalc/CcsCeffDelayCalc.cc
@@ -87,7 +87,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
vth_ = drvr_library->outputThreshold(rf) * vdd_;
vl_ = drvr_library->slewLowerThreshold(rf) * vdd_;
vh_ = drvr_library->slewUpperThreshold(rf) * vdd_;
- in_slew_ = in_slew;
+ in_slew_ = delayAsFloat(in_slew);
load_cap_ = load_cap;
parasitic_ = parasitic;
drvr_cell->ensureVoltageWaveforms();
diff --git a/dcalc/CcsSimDelayCalc.cc b/dcalc/CcsSimDelayCalc.cc
new file mode 100644
index 00000000..7c4a6224
--- /dev/null
+++ b/dcalc/CcsSimDelayCalc.cc
@@ -0,0 +1,901 @@
+// OpenSTA, Static Timing Analyzer
+// Copyright (c) 2023, Parallax Software, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "CcsSimDelayCalc.hh"
+
+#include // abs
+
+#include "Debug.hh"
+#include "Units.hh"
+#include "TimingArc.hh"
+#include "Liberty.hh"
+#include "Sdc.hh"
+#include "Parasitics.hh"
+#include "DcalcAnalysisPt.hh"
+#include "Network.hh"
+#include "Corner.hh"
+#include "Graph.hh"
+#include "GraphDelayCalc.hh"
+#include "DmpDelayCalc.hh"
+
+namespace sta {
+
+using std::abs;
+using std::make_shared;
+
+// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
+// McGraw-Hill, Inc. New York, NY.
+
+ArcDelayCalc *
+makeCcsSimDelayCalc(StaState *sta)
+{
+ return new CcsSimDelayCalc(sta);
+}
+
+CcsSimDelayCalc::CcsSimDelayCalc(StaState *sta) :
+ DelayCalcBase(sta),
+ dcalc_args_(nullptr),
+ load_pin_index_map_(network_),
+ dcalc_failed_(false),
+ pin_node_map_(network_),
+ make_waveforms_(false),
+ waveform_drvr_pin_(nullptr),
+ waveform_load_pin_(nullptr),
+ table_dcalc_(makeDmpCeffElmoreDelayCalc(sta))
+{
+}
+
+CcsSimDelayCalc::~CcsSimDelayCalc()
+{
+ delete table_dcalc_;
+}
+
+ArcDelayCalc *
+CcsSimDelayCalc::copy()
+{
+ return new CcsSimDelayCalc(this);
+}
+
+Parasitic *
+CcsSimDelayCalc::findParasitic(const Pin *drvr_pin,
+ const RiseFall *,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ const Corner *corner = dcalc_ap->corner();
+ Parasitic *parasitic = nullptr;
+ // set_load net has precidence over parasitics.
+ if (!sdc_->drvrPinHasWireCap(drvr_pin, corner)) {
+ const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt();
+ if (parasitics_->haveParasitics())
+ parasitic = parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap);
+ }
+ return parasitic;
+}
+
+Parasitic *
+CcsSimDelayCalc::reduceParasitic(const Parasitic *parasitic_network,
+ const Pin *,
+ const RiseFall *,
+ const DcalcAnalysisPt *)
+{
+ return const_cast(parasitic_network);
+}
+
+ArcDcalcResult
+CcsSimDelayCalc::inputPortDelay(const Pin *drvr_pin,
+ float in_slew,
+ const RiseFall *rf,
+ const Parasitic *parasitic,
+ const LoadPinIndexMap &load_pin_index_map,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ ArcDcalcResult dcalc_result(load_pin_index_map.size());
+ LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
+
+ const Parasitic *pi_elmore = nullptr;
+ if (parasitic && parasitics_->isParasiticNetwork(parasitic)) {
+ const ParasiticAnalysisPt *ap = dcalc_ap->parasiticAnalysisPt();
+ parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf,
+ dcalc_ap->corner(),
+ dcalc_ap->constraintMinMax(), ap);
+ pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
+ }
+
+ for (auto load_pin_index : load_pin_index_map) {
+ const Pin *load_pin = load_pin_index.first;
+ size_t load_idx = load_pin_index.second;
+ ArcDelay wire_delay = 0.0;
+ Slew load_slew = in_slew;
+ bool elmore_exists = false;
+ float elmore = 0.0;
+ if (pi_elmore)
+ parasitics_->findElmore(pi_elmore, load_pin, elmore, elmore_exists);
+ if (elmore_exists)
+ // Input port with no external driver.
+ dspfWireDelaySlew(load_pin, rf, in_slew, elmore, wire_delay, load_slew);
+ thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
+ dcalc_result.setWireDelay(load_idx, wire_delay);
+ dcalc_result.setLoadSlew(load_idx, load_slew);
+ }
+ return dcalc_result;
+}
+
+ArcDcalcResult
+CcsSimDelayCalc::gateDelay(const Pin *drvr_pin,
+ const TimingArc *arc,
+ const Slew &in_slew,
+ float load_cap,
+ const Parasitic *parasitic,
+ const LoadPinIndexMap &load_pin_index_map,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
+ load_pin_index_map_ = graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
+ 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_);
+
+ // Assume drivers are in the same library.
+ LibertyCell *drvr_cell = dcalc_args[0].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());
+ vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
+ vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
+ vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
+ drvr_cell->ensureVoltageWaveforms();
+
+ 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());
+ }
+ 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);
+
+ for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
+ ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
+ ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx];
+ const Pin *drvr_pin = dcalc_arg.drvrPin();
+ size_t drvr_node = pin_node_map_[drvr_pin];
+ ThresholdTimes &drvr_times = threshold_times_[drvr_node];
+ ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time_[drvr_idx];
+ Slew drvr_slew = abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]);
+ dcalc_result.setGateDelay(gate_delay);
+ dcalc_result.setDrvrSlew(drvr_slew);
+ debugPrint(debug_, "ccs_dcalc", 2,
+ "%s gate delay %s slew %s",
+ network_->pathName(drvr_pin),
+ delayAsString(gate_delay, this),
+ delayAsString(drvr_slew, this));
+
+ dcalc_result.setLoadCount(load_pin_index_map.size());
+ for (auto load_pin_index : load_pin_index_map) {
+ const Pin *load_pin = load_pin_index.first;
+ size_t load_idx = load_pin_index.second;
+ size_t load_node = pin_node_map_[load_pin];
+ ThresholdTimes &wire_times = threshold_times_[load_node];
+ ThresholdTimes &drvr_times = threshold_times_[drvr_node];
+ ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
+ Slew load_slew = abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
+ debugPrint(debug_, "ccs_dcalc", 2,
+ "load %s %s delay %s slew %s",
+ network_->pathName(load_pin),
+ drvr_rf_->asString(),
+ delayAsString(wire_delay, this),
+ delayAsString(load_slew, this));
+
+ thresholdAdjust(load_pin, drvr_library, drvr_rf_, wire_delay, load_slew);
+ dcalc_result.setWireDelay(load_idx, wire_delay);
+ dcalc_result.setLoadSlew(load_idx, load_slew);
+ }
+ }
+ }
+ return dcalc_results;
+}
+
+void
+CcsSimDelayCalc::simulate(ArcDcalcArgSeq &dcalc_args)
+{
+ const Pin *drvr_pin = dcalc_args[0].drvrPin();
+ LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
+ const MinMax *min_max = dcalc_ap_->delayMinMax();
+ drive_resistance_ = drvr_port->driveResistance(drvr_rf_, min_max);
+
+ initSim();
+ stampConductances();
+
+ // The conductance matrix does not change as long as the time step is constant.
+ // Factor stamping and LU decomposition of the conductance matrix
+ // outside of the simulation loop.
+ // Prevent copying of matrix.
+ conductances_.makeCompressed();
+ // LU factor conductances.
+ solver_.compute(conductances_);
+
+ for (size_t drvr_idx = 0; drvr_idx < dcalc_args.size(); drvr_idx++) {
+ ArcDcalcArg &dcalc_arg = dcalc_args[drvr_idx];
+ // Find initial ceff.
+ ceff_[drvr_idx] = load_cap_;
+ // voltageTime is always for a rising waveform so 0.0v is initial voltage.
+ drvr_current_[drvr_idx] =
+ output_waveforms_[drvr_idx]->voltageCurrent(delayAsFloat(dcalc_arg.inSlew()),
+ ceff_[drvr_idx], 0.0);
+ }
+ // Initial time depends on ceff which impact delay, so use a sim step
+ // to find an initial ceff.
+ setCurrents();
+ voltages_ = solver_.solve(currents_);
+ updateCeffIdrvr();
+ initNodeVoltages();
+
+ // voltageTime is always for a rising waveform so 0.0v is initial voltage.
+ double time_begin = output_waveforms_[0]->voltageTime(dcalc_args[0].inSlewFlt(),
+ ceff_[0], 0.0);
+ // Limit in case load voltage waveforms don't get to final value.
+ double time_end = time_begin + maxTime();
+
+ if (make_waveforms_)
+ recordWaveformStep(time_begin);
+
+ for (double time = time_begin; time <= time_end; time += time_step_) {
+ stampConductances();
+ conductances_.makeCompressed();
+ solver_.compute(conductances_);
+ setCurrents();
+ voltages_ = solver_.solve(currents_);
+
+ debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s",
+ delayAsString(time, this),
+ units_->capacitanceUnit()->asString(ceff_[0]),
+ voltages_[pin_node_map_[dcalc_args[0].drvrPin()]],
+ units_->currentUnit()->asString(drvr_current_[0], 4));
+
+ updateCeffIdrvr();
+ measureThresholds(time);
+ if (make_waveforms_)
+ recordWaveformStep(time);
+
+ bool loads_finished = true;
+ for (auto load_node1 : pin_node_map_) {
+ size_t load_node = load_node1.second;
+ if ((drvr_rf_ == RiseFall::rise()
+ && voltages_[load_node] < vh_ + (vdd_ - vh_) * .5)
+ || (drvr_rf_ == RiseFall::fall()
+ && (voltages_[load_node] > vl_ * .5))) {
+ loads_finished = false;
+ break;
+ }
+ }
+ if (loads_finished)
+ break;
+
+ time_step_prev_ = time_step_;
+ // swap faster than copying with '='.
+ voltages_prev2_.swap(voltages_prev1_);
+ voltages_prev1_.swap(voltages_);
+ }
+}
+
+double
+CcsSimDelayCalc::timeStep()
+{
+ // Needs to use LTE for time step dynamic control.
+ return drive_resistance_ * load_cap_ * .02;
+}
+
+double
+CcsSimDelayCalc::maxTime()
+{
+ return (*dcalc_args_)[0].inSlewFlt()
+ + (drive_resistance_ + resistance_sum_) * load_cap_ * 2;
+}
+
+void
+CcsSimDelayCalc::initSim()
+{
+ ceff_.resize(drvr_count_);
+ drvr_current_.resize(drvr_count_);
+
+ findNodeCount();
+ setOrder();
+
+ initNodeVoltages();
+
+ // time step required by initCapacitanceCurrents
+ time_step_ = time_step_prev_ = timeStep();
+ debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this));
+
+ // Reset waveform recording.
+ times_.clear();
+ drvr_voltages_.clear();
+ load_voltages_.clear();
+
+ measure_thresholds_ = {vl_, vth_, vh_};
+}
+
+void
+CcsSimDelayCalc::findNodeCount()
+{
+ includes_pin_caps_ = parasitics_->includesPinCaps(parasitic_network_);
+ coupling_cap_multiplier_ = 1.0;
+
+ node_capacitances_.clear();
+ pin_node_map_.clear();
+ node_index_map_.clear();
+
+ for (ParasiticNode *node : parasitics_->nodes(parasitic_network_)) {
+ if (!parasitics_->isExternal(node)) {
+ size_t node_idx = node_index_map_.size();
+ node_index_map_[node] = node_idx;
+ const Pin *pin = parasitics_->pin(node);
+ if (pin) {
+ pin_node_map_[pin] = node_idx;
+ debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu",
+ network_->pathName(pin),
+ node_idx);
+ }
+ double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
+ node_capacitances_.push_back(cap);
+ }
+ }
+
+ for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) {
+ float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
+ ParasiticNode *node1 = parasitics_->node1(capacitor);
+ if (!parasitics_->isExternal(node1)) {
+ size_t node_idx = node_index_map_[node1];
+ node_capacitances_[node_idx] += cap;
+ }
+ ParasiticNode *node2 = parasitics_->node2(capacitor);
+ if (!parasitics_->isExternal(node2)) {
+ size_t node_idx = node_index_map_[node2];
+ node_capacitances_[node_idx] += cap;
+ }
+ }
+ node_count_ = node_index_map_.size();
+}
+
+float
+CcsSimDelayCalc::pinCapacitance(ParasiticNode *node)
+{
+ const Pin *pin = parasitics_->pin(node);
+ float pin_cap = 0.0;
+ if (pin) {
+ Port *port = network_->port(pin);
+ LibertyPort *lib_port = network_->libertyPort(port);
+ const Corner *corner = dcalc_ap_->corner();
+ const MinMax *cnst_min_max = dcalc_ap_->constraintMinMax();
+ if (lib_port) {
+ if (!includes_pin_caps_)
+ pin_cap = sdc_->pinCapacitance(pin, drvr_rf_, corner, cnst_min_max);
+ }
+ else if (network_->isTopLevelPort(pin))
+ pin_cap = sdc_->portExtCap(port, drvr_rf_, corner, cnst_min_max);
+ }
+ return pin_cap;
+}
+
+void
+CcsSimDelayCalc::setOrder()
+{
+ currents_.resize(node_count_);
+ voltages_.resize(node_count_);
+ voltages_prev1_.resize(node_count_);
+ voltages_prev2_.resize(node_count_);
+ // Matrix resize also zeros.
+ conductances_.resize(node_count_, node_count_);
+ threshold_times_.resize(node_count_);
+}
+
+void
+CcsSimDelayCalc::initNodeVoltages()
+{
+ double drvr_init_volt = (drvr_rf_ == RiseFall::rise()) ? 0.0 : vdd_;
+ for (size_t i = 0; i < node_count_; i++) {
+ voltages_[i] = drvr_init_volt;
+ voltages_prev1_[i] = drvr_init_volt;
+ voltages_prev2_[i] = drvr_init_volt;
+ }
+}
+
+void
+CcsSimDelayCalc::simulateStep()
+{
+ setCurrents();
+ voltages_ = solver_.solve(currents_);
+}
+
+void
+CcsSimDelayCalc::stampConductances()
+{
+ conductances_.setZero();
+ for (size_t node_idx = 0; node_idx < node_count_; node_idx++)
+ stampCapacitance(node_idx, node_capacitances_[node_idx]);
+
+ resistance_sum_ = 0.0;
+ for (ParasiticResistor *resistor : parasitics_->resistors(parasitic_network_)) {
+ ParasiticNode *node1 = parasitics_->node1(resistor);
+ ParasiticNode *node2 = parasitics_->node2(resistor);
+ // One commercial extractor creates resistors with identical from/to nodes.
+ if (node1 != node2) {
+ size_t node_idx1 = node_index_map_[node1];
+ size_t node_idx2 = node_index_map_[node2];
+ float resistance = parasitics_->value(resistor);
+ stampConductance(node_idx1, node_idx2, 1.0 / resistance);
+ resistance_sum_ += resistance;
+ }
+ }
+}
+
+// Grounded resistor.
+void
+CcsSimDelayCalc::stampConductance(size_t n1,
+ double g)
+{
+ conductances_.coeffRef(n1, n1) += g;
+}
+
+// Floating resistor.
+void
+CcsSimDelayCalc::stampConductance(size_t n1,
+ size_t n2,
+ double g)
+{
+ conductances_.coeffRef(n1, n1) += g;
+ conductances_.coeffRef(n2, n2) += g;
+ conductances_.coeffRef(n1, n2) -= g;
+ conductances_.coeffRef(n2, n1) -= g;
+}
+
+// Grounded capacitance.
+void
+CcsSimDelayCalc::stampCapacitance(size_t n1,
+ double cap)
+{
+ double g = cap * 2.0 / time_step_;
+ stampConductance(n1, g);
+}
+
+// Floating capacitance.
+void
+CcsSimDelayCalc::stampCapacitance(size_t n1,
+ size_t n2,
+ double cap)
+{
+ double g = cap * 2.0 / time_step_;
+ stampConductance(n1, n2, g);
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+CcsSimDelayCalc::setCurrents()
+{
+ currents_.setZero(node_count_);
+ for (size_t i = 0; i < drvr_count_; i++) {
+ size_t drvr_node = pin_node_map_[(*dcalc_args_)[i].drvrPin()];
+ insertCurrentSrc(drvr_node, drvr_current_[i]);
+ }
+
+ for (size_t node_idx = 0; node_idx < node_count_; node_idx++)
+ insertCapCurrentSrc(node_idx, node_capacitances_[node_idx]);
+}
+
+void
+CcsSimDelayCalc::insertCapCurrentSrc(size_t n1,
+ double cap)
+{
+ // Direct implementation of figure 4.11 in
+ // “Electronic Circuit & System Simulation Methods” allowing for time
+ // step changes.
+ // double g0 = 2.0 * cap / time_step_;
+ // double g1 = 2.0 * cap / time_step_prev_;
+ // double dv = voltages_prev2_[n1] - voltages_prev1_[n1];
+ // double ieq_prev = cap * dv / time_step_ + g0 * voltages_prev1_[n1];
+ // double i_cap = (g0 + g1) * voltages_prev1_[n1] - ieq_prev;
+
+ // Above simplified.
+ // double i_cap
+ // = cap / time_step_ * voltages_prev1_[n1]
+ // + 2.0 * cap / time_step_prev_ * voltages_prev1_[n1]
+ // - cap / time_step_ * voltages_prev2_[n1];
+
+ // Simplified for constant time step.
+ double i_cap
+ = 3.0 * cap / time_step_ * voltages_prev1_[n1]
+ - cap / time_step_ * voltages_prev2_[n1];
+ insertCurrentSrc(n1, i_cap);
+}
+
+void
+CcsSimDelayCalc::insertCapaCurrentSrc(size_t n1,
+ size_t n2,
+ double cap)
+{
+ double g0 = 2.0 * cap / time_step_;
+ double g1 = 2.0 * cap / time_step_prev_;
+ double dv = (voltages_prev2_[n1] - voltages_prev2_[n2])
+ - (voltages_prev1_[n1] - voltages_prev1_[n2]);
+ double ieq_prev = cap * dv / time_step_ + g0*(voltages_prev1_[n1]-voltages_prev1_[n2]);
+ double i_cap = (g0 + g1) * (voltages_prev1_[n1] - voltages_prev1_[n2]) - ieq_prev;
+ insertCurrentSrc(n1, n2, i_cap);
+}
+
+void
+CcsSimDelayCalc::insertCurrentSrc(size_t n1,
+ double current)
+{
+ currents_.coeffRef(n1) += current;
+}
+
+void
+CcsSimDelayCalc::insertCurrentSrc(size_t n1,
+ size_t n2,
+ double current)
+{
+ currents_.coeffRef(n1) += current;
+ currents_.coeffRef(n2) -= current;
+}
+
+void
+CcsSimDelayCalc::updateCeffIdrvr()
+{
+ for (size_t i = 0; i < drvr_count_; i++) {
+ size_t drvr_node = pin_node_map_[(*dcalc_args_)[i].drvrPin()];
+ double dv = voltages_[drvr_node] - voltages_prev1_[drvr_node];
+ if (drvr_rf_ == RiseFall::rise()) {
+ if (drvr_current_[i] != 0.0
+ && dv > 0.0) {
+ double ceff = drvr_current_[i] * time_step_ / dv;
+ if (output_waveforms_[i]->capAxis()->inBounds(ceff))
+ ceff_[i] = ceff;
+ }
+
+ double v = voltages_[drvr_node];
+ if (voltages_[drvr_node] > (vdd_ - .01))
+ // Whoa partner. Head'n for the weeds.
+ drvr_current_[i] = 0.0;
+ else
+ drvr_current_[i] =
+ output_waveforms_[i]->voltageCurrent((*dcalc_args_)[i].inSlewFlt(),
+ ceff_[i], v);
+ }
+ else {
+ if (drvr_current_[i] != 0.0
+ && dv < 0.0) {
+ double ceff = drvr_current_[i] * time_step_ / dv;
+ if (output_waveforms_[i]->capAxis()->inBounds(ceff))
+ ceff_[i] = ceff;
+ }
+ double v = vdd_ - voltages_[drvr_node];
+ if (voltages_[drvr_node] < 0.01)
+ // Whoa partner. Head'n for the weeds.
+ drvr_current_[i] = 0.0;
+ else
+ drvr_current_[i] =
+ output_waveforms_[i]->voltageCurrent((*dcalc_args_)[i].inSlewFlt(),
+ ceff_[i], v);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+CcsSimDelayCalc::measureThresholds(double time)
+{
+ for (auto pin_node1 : pin_node_map_) {
+ size_t pin_node = pin_node1.second;
+ measureThresholds(time, pin_node);
+ }
+}
+
+void
+CcsSimDelayCalc::measureThresholds(double time,
+ size_t n)
+{
+ double v = voltages_[n];
+ double v_prev = voltages_prev1_[n];
+ for (size_t m = 0; m < measure_threshold_count_; m++) {
+ double th = measure_thresholds_[m];
+ if ((v_prev < th && th <= v)
+ || (v_prev > th && th >= v)) {
+ double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
+ debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s",
+ n,
+ th,
+ delayAsString(t_cross, this));
+ threshold_times_[n][m] = t_cross;
+ }
+ }
+}
+
+void
+CcsSimDelayCalc::recordWaveformStep(double time)
+{
+ times_.push_back(time);
+ size_t drvr_node = pin_node_map_[waveform_drvr_pin_];
+ drvr_voltages_.push_back(voltages_[drvr_node]);
+ if (waveform_load_pin_) {
+ size_t load_node = pin_node_map_[waveform_load_pin_];
+ load_voltages_.push_back(voltages_[load_node]);
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+string
+CcsSimDelayCalc::reportGateDelay(const Pin *drvr_pin,
+ const TimingArc *arc,
+ const Slew &in_slew,
+ float load_cap,
+ const Parasitic *,
+ const LoadPinIndexMap &,
+ const DcalcAnalysisPt *dcalc_ap,
+ int digits)
+{
+ GateTimingModel *model = gateModel(arc, dcalc_ap);
+ if (model) {
+ float in_slew1 = delayAsFloat(in_slew);
+ return model->reportGateDelay(pinPvt(drvr_pin, dcalc_ap), in_slew1, load_cap,
+ false, digits);
+ }
+ return "";
+}
+
+////////////////////////////////////////////////////////////////
+
+// Waveform accessors for swig/tcl.
+Table1
+CcsSimDelayCalc::drvrWaveform(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ const Corner *corner,
+ const MinMax *min_max)
+{
+ makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, nullptr, corner, min_max);
+ TableAxisPtr time_axis = make_shared(TableAxisVariable::time,
+ new FloatSeq(times_));
+ Table1 waveform(new FloatSeq(drvr_voltages_), time_axis);
+ return waveform;
+}
+
+Table1
+CcsSimDelayCalc::loadWaveform(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ const Pin *load_pin,
+ const Corner *corner,
+ const MinMax *min_max)
+{
+ makeWaveforms(in_pin, in_rf, drvr_pin, drvr_rf, load_pin, corner, min_max);
+ TableAxisPtr time_axis = make_shared(TableAxisVariable::time,
+ new FloatSeq(times_));
+ Table1 waveform(new FloatSeq(load_voltages_), time_axis);
+ return waveform;
+}
+
+Table1
+CcsSimDelayCalc::inputWaveform(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Corner *corner,
+ const MinMax *min_max)
+{
+ LibertyPort *port = network_->libertyPort(in_pin);
+ if (port) {
+ DriverWaveform *driver_waveform = port->driverWaveform(in_rf);
+ const Vertex *in_vertex = graph_->pinLoadVertex(in_pin);
+ DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
+ float in_slew = delayAsFloat(graph_->slew(in_vertex, in_rf, dcalc_ap->index()));
+ LibertyLibrary *library = port->libertyLibrary();
+ float vdd;
+ bool vdd_exists;
+ library->supplyVoltage("VDD", vdd, vdd_exists);
+ if (!vdd_exists)
+ report_->error(1721, "VDD not defined in library %s", library->name());
+ Table1 in_waveform = driver_waveform->waveform(in_slew);
+ // Scale the waveform from 0:vdd.
+ FloatSeq *scaled_values = new FloatSeq;
+ for (float value : *in_waveform.values())
+ scaled_values->push_back(value * vdd);
+ return Table1(scaled_values, in_waveform.axis1ptr());
+ }
+ return Table1();
+}
+
+void
+CcsSimDelayCalc::makeWaveforms(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ const Pin *load_pin,
+ const Corner *corner,
+ const MinMax *min_max)
+{
+ Edge *edge;
+ const TimingArc *arc;
+ graph_->gateEdgeArc(in_pin, in_rf, drvr_pin, drvr_rf, edge, arc);
+ if (arc) {
+ DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
+ const Parasitic *parasitic = findParasitic(drvr_pin, drvr_rf, dcalc_ap);
+ if (parasitic) {
+ make_waveforms_ = true;
+ waveform_drvr_pin_ = drvr_pin;
+ waveform_load_pin_ = load_pin;
+ Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin);
+ graph_delay_calc_->findDriverArcDelays(drvr_vertex, edge, arc, dcalc_ap, this);
+ make_waveforms_ = false;
+ waveform_drvr_pin_ = nullptr;
+ waveform_load_pin_ = nullptr;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+CcsSimDelayCalc::reportMatrix(const char *name,
+ MatrixSd &matrix)
+{
+ report_->reportLine("%s", name);
+ reportMatrix(matrix);
+}
+
+void
+CcsSimDelayCalc::reportMatrix(const char *name,
+ MatrixXd &matrix)
+{
+ report_->reportLine("%s", name);
+ reportMatrix(matrix);
+}
+
+void
+CcsSimDelayCalc::reportMatrix(const char *name,
+ VectorXd &matrix)
+{
+ report_->reportLine("%s", name);
+ reportMatrix(matrix);
+}
+
+void
+CcsSimDelayCalc::reportVector(const char *name,
+ vector &matrix)
+{
+ report_->reportLine("%s", name);
+ reportVector(matrix);
+}
+
+void
+CcsSimDelayCalc::reportMatrix(MatrixSd &matrix)
+{
+ for (Index i = 0; i < matrix.rows(); i++) {
+ string line = "| ";
+ for (Index j = 0; j < matrix.cols(); j++) {
+ string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
+ line += entry;
+ line += " ";
+ }
+ line += "|";
+ report_->reportLineString(line);
+ }
+}
+
+void
+CcsSimDelayCalc::reportMatrix(MatrixXd &matrix)
+{
+ for (Index i = 0; i < matrix.rows(); i++) {
+ string line = "| ";
+ for (Index j = 0; j < matrix.cols(); j++) {
+ string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
+ line += entry;
+ line += " ";
+ }
+ line += "|";
+ report_->reportLineString(line);
+ }
+}
+
+void
+CcsSimDelayCalc::reportMatrix(VectorXd &matrix)
+{
+ string line = "| ";
+ for (Index i = 0; i < matrix.rows(); i++) {
+ string entry = stdstrPrint("%10.3e", matrix.coeff(i));
+ line += entry;
+ line += " ";
+ }
+ line += "|";
+ report_->reportLineString(line);
+}
+
+void
+CcsSimDelayCalc::reportVector(vector &matrix)
+{
+ string line = "| ";
+ for (size_t i = 0; i < matrix.size(); i++) {
+ string entry = stdstrPrint("%10.3e", matrix[i]);
+ line += entry;
+ line += " ";
+ }
+ line += "|";
+ report_->reportLineString(line);
+}
+
+} // namespace
diff --git a/dcalc/CcsSimDelayCalc.hh b/dcalc/CcsSimDelayCalc.hh
new file mode 100644
index 00000000..5f000472
--- /dev/null
+++ b/dcalc/CcsSimDelayCalc.hh
@@ -0,0 +1,238 @@
+// OpenSTA, Static Timing Analyzer
+// Copyright (c) 2023, Parallax Software, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#pragma once
+
+#include
+#include
+#include
+
+#include "LumpedCapDelayCalc.hh"
+#include "ArcDcalcWaveforms.hh"
+
+namespace sta {
+
+class ArcDelayCalc;
+class StaState;
+class Corner;
+
+using std::vector;
+using std::array;
+using Eigen::MatrixXd;
+using Eigen::MatrixXcd;
+using Eigen::VectorXd;
+using Eigen::SparseMatrix;
+using Eigen::Index;
+using Eigen::SparseLU;
+
+typedef Map PinNodeMap;
+typedef Map NodeIndexMap;
+typedef Map PortIndexMap;
+typedef SparseMatrix MatrixSd;
+
+ArcDelayCalc *
+makeCcsSimDelayCalc(StaState *sta);
+
+class CcsSimDelayCalc : public DelayCalcBase, public ArcDcalcWaveforms
+{
+public:
+ CcsSimDelayCalc(StaState *sta);
+ ~CcsSimDelayCalc();
+ ArcDelayCalc *copy() override;
+ Parasitic *findParasitic(const Pin *drvr_pin,
+ const RiseFall *rf,
+ const DcalcAnalysisPt *dcalc_ap) override;
+ Parasitic *reduceParasitic(const Parasitic *parasitic_network,
+ const Pin *drvr_pin,
+ const RiseFall *rf,
+ const DcalcAnalysisPt *dcalc_ap) override;
+ ArcDcalcResult inputPortDelay(const Pin *drvr_pin,
+ float in_slew,
+ const RiseFall *rf,
+ const Parasitic *parasitic,
+ const LoadPinIndexMap &load_pin_index_map,
+ const DcalcAnalysisPt *dcalc_ap) override;
+ ArcDcalcResult gateDelay(const Pin *drvr_pin,
+ const TimingArc *arc,
+ const Slew &in_slew,
+ float load_cap,
+ const Parasitic *parasitic,
+ const LoadPinIndexMap &load_pin_index_map,
+ const DcalcAnalysisPt *dcalc_ap) override;
+ ArcDcalcResultSeq gateDelays(ArcDcalcArgSeq &dcalc_args,
+ float load_cap,
+ const LoadPinIndexMap &load_pin_index_map,
+ const DcalcAnalysisPt *dcalc_ap) override;
+ string reportGateDelay(const Pin *drvr_pin,
+ const TimingArc *arc,
+ const Slew &in_slew,
+ float load_cap,
+ const Parasitic *parasitic,
+ const LoadPinIndexMap &load_pin_index_map,
+ const DcalcAnalysisPt *dcalc_ap,
+ int digits) override;
+
+ Table1 inputWaveform(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Corner *corner,
+ const MinMax *min_max) override;
+ Table1 drvrWaveform(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ const Corner *corner,
+ const MinMax *min_max) override;
+ Table1 loadWaveform(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ const Pin *load_pin,
+ const Corner *corner,
+ const MinMax *min_max) override;
+
+protected:
+ void simulate(ArcDcalcArgSeq &dcalc_args);
+ virtual double maxTime();
+ virtual double timeStep();
+ void updateCeffIdrvr();
+ void initSim();
+ void findLoads();
+ virtual void findNodeCount();
+ void setOrder();
+ void initNodeVoltages();
+ void simulateStep();
+ virtual void stampConductances();
+ void stampConductance(size_t n1,
+ double g);
+ void stampConductance(size_t n1,
+ size_t n2,
+ double g);
+ void stampCapacitance(size_t n1,
+ double cap);
+ void stampCapacitance(size_t n1,
+ size_t n2,
+ double cap);
+ float pinCapacitance(ParasiticNode *node);
+ virtual void setCurrents();
+ void insertCapCurrentSrc(size_t n1,
+ double cap);
+ void insertCapaCurrentSrc(size_t n1,
+ size_t n2,
+ double cap);
+ void insertCurrentSrc(size_t n1,
+ double current);
+ void insertCurrentSrc(size_t n1,
+ size_t n2,
+ double current);
+ void measureThresholds(double time);
+ void measureThresholds(double time,
+ size_t i);
+ void loadDelaySlew(const Pin *load_pin,
+ // Return values.
+ ArcDelay &delay,
+ Slew &slew);
+ void recordWaveformStep(double time);
+ void makeWaveforms(const Pin *in_pin,
+ const RiseFall *in_rf,
+ const Pin *drvr_pin,
+ const RiseFall *drvr_rf,
+ const Pin *load_pin,
+ const Corner *corner,
+ const MinMax *min_max);
+ void reportMatrix(const char *name,
+ MatrixSd &matrix);
+ void reportMatrix(const char *name,
+ MatrixXd &matrix);
+ void reportMatrix(const char *name,
+ VectorXd &matrix);
+ void reportVector(const char *name,
+ vector &matrix);
+ void reportMatrix(MatrixSd &matrix);
+ void reportMatrix(MatrixXd &matrix);
+ void reportMatrix(VectorXd &matrix);
+ void reportVector(vector &matrix);
+
+ ArcDcalcArgSeq *dcalc_args_;
+ size_t drvr_count_;
+ float load_cap_;
+ const DcalcAnalysisPt *dcalc_ap_;
+ const Parasitic *parasitic_network_;
+ const RiseFall *drvr_rf_;
+
+ // Tmp for gateDelay/loadDelay api.
+ ArcDcalcResult dcalc_result_;
+ LoadPinIndexMap load_pin_index_map_;
+
+ bool dcalc_failed_;
+ size_t node_count_; // Parasitic network node count
+ PinNodeMap pin_node_map_; // Parasitic pin -> array index
+ NodeIndexMap node_index_map_; // Parasitic node -> array index
+ vector output_waveforms_;
+ vector ref_time_;
+ double drive_resistance_;
+ double resistance_sum_;
+
+ vector node_capacitances_;
+ bool includes_pin_caps_;
+ float coupling_cap_multiplier_;
+
+ // Indexed by driver index.
+ vector ceff_;
+ vector drvr_current_;
+
+ double time_step_;
+ double time_step_prev_;
+ // I = GV
+ // currents_ = conductances_ * voltages_
+ VectorXd currents_;
+ MatrixSd conductances_;
+ VectorXd voltages_;
+ VectorXd voltages_prev1_;
+ VectorXd voltages_prev2_;
+ SparseLU solver_;
+
+ // Waveform recording.
+ bool make_waveforms_;
+ const Pin *waveform_drvr_pin_;
+ const Pin *waveform_load_pin_;
+ FloatSeq drvr_voltages_;
+ FloatSeq load_voltages_;
+ FloatSeq times_;
+
+ size_t drvr_idx_;
+
+ float vdd_;
+ float vth_;
+ float vl_;
+ float vh_;
+
+ static constexpr size_t threshold_vl = 0;
+ static constexpr size_t threshold_vth = 1;
+ static constexpr size_t threshold_vh = 2;
+ static constexpr size_t measure_threshold_count_ = 3;
+ typedef array ThresholdTimes;
+ // Vl Vth Vh
+ ThresholdTimes measure_thresholds_;
+ // Indexed by node number.
+ vector threshold_times_;
+
+ // Delay calculator to use when ccs waveforms are missing from liberty.
+ ArcDelayCalc *table_dcalc_;
+
+ using ArcDelayCalc::reduceParasitic;
+};
+
+} // namespacet
diff --git a/dcalc/DelayCalc.cc b/dcalc/DelayCalc.cc
index 2fe5dcd5..61b3f007 100644
--- a/dcalc/DelayCalc.cc
+++ b/dcalc/DelayCalc.cc
@@ -23,6 +23,7 @@
#include "DmpDelayCalc.hh"
#include "ArnoldiDelayCalc.hh"
#include "CcsCeffDelayCalc.hh"
+#include "CcsSimDelayCalc.hh"
namespace sta {
@@ -39,6 +40,7 @@ registerDelayCalcs()
registerDelayCalc("dmp_ceff_two_pole", makeDmpCeffTwoPoleDelayCalc);
registerDelayCalc("arnoldi", makeArnoldiDelayCalc);
registerDelayCalc("ccs_ceff", makeCcsCeffDelayCalc);
+ registerDelayCalc("ccs_sim", makeCcsSimDelayCalc);
}
void
diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc
index 9d8f8435..8a96730f 100644
--- a/dcalc/GraphDelayCalc.cc
+++ b/dcalc/GraphDelayCalc.cc
@@ -1413,12 +1413,14 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
int slew_index = dcalc_ap->checkDataSlewIndex();
const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index);
debugPrint(debug_, "delay_calc", 3,
- " %s %s -> %s %s (%s)",
+ " %s %s -> %s %s (%s) corner:%s/%s",
arc_set->from()->name(),
arc->fromEdge()->asString(),
arc_set->to()->name(),
arc->toEdge()->asString(),
- arc_set->role()->asString());
+ arc_set->role()->asString(),
+ dcalc_ap->corner()->name(),
+ dcalc_ap->delayMinMax()->asString());
debugPrint(debug_, "delay_calc", 3,
" from_slew = %s to_slew = %s",
delayAsString(from_slew, this),
@@ -1426,7 +1428,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
float related_out_cap = 0.0;
if (related_out_pin)
related_out_cap = loadCap(related_out_pin, to_rf,dcalc_ap,arc_delay_calc);
- ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew,
+ ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew,
to_slew, related_out_cap,
dcalc_ap);
debugPrint(debug_, "delay_calc", 3,
@@ -1512,30 +1514,6 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge,
////////////////////////////////////////////////////////////////
-void
-GraphDelayCalc::minPulseWidth(const Pin *pin,
- const RiseFall *hi_low,
- DcalcAPIndex ap_index,
- const MinMax *min_max,
- // Return values.
- float &min_width,
- bool &exists)
-{
- // Sdf annotation.
- graph_->widthCheckAnnotation(pin, hi_low, ap_index,
- min_width, exists);
- if (!exists) {
- // Liberty library.
- LibertyPort *port = network_->libertyPort(pin);
- if (port) {
- Instance *inst = network_->instance(pin);
- const Pvt *pvt = inst ? sdc_->pvt(inst, min_max) : nullptr;
- OperatingConditions *op_cond=sdc_->operatingConditions(min_max);
- port->minPulseWidth(hi_low, op_cond, pvt, min_width, exists);
- }
- }
-}
-
void
GraphDelayCalc::minPeriod(const Pin *pin,
// Return values.
diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt
index 2682fccf..44c18ac5 100644
Binary files a/doc/OpenSTA.odt and b/doc/OpenSTA.odt differ
diff --git a/doc/OpenSTA.pdf b/doc/OpenSTA.pdf
index d0e5f76e..b09c49c2 100644
Binary files a/doc/OpenSTA.pdf and b/doc/OpenSTA.pdf differ
diff --git a/graph/Graph.cc b/graph/Graph.cc
index 3ce7aec6..591a2271 100644
--- a/graph/Graph.cc
+++ b/graph/Graph.cc
@@ -47,7 +47,6 @@ Graph::Graph(StaState *sta,
slew_rf_count_(slew_rf_count),
have_arc_delays_(have_arc_delays),
ap_count_(ap_count),
- width_check_annotations_(nullptr),
period_check_annotations_(nullptr),
reg_clk_vertices_(new VertexSet(graph_))
{
@@ -62,7 +61,6 @@ Graph::~Graph()
delete reg_clk_vertices_;
deleteSlewTables();
deleteArcDelayTables();
- removeWidthCheckAnnotations();
removePeriodCheckAnnotations();
}
@@ -205,9 +203,9 @@ Graph::makePortInstanceEdges(const Instance *inst,
// Vertices can be missing from the graph if the pins
// are power or ground.
if (from_vertex) {
- bool is_check = arc_set->role()->isTimingCheck();
- if (to_bidirect_drvr_vertex &&
- !is_check)
+ TimingRole *role = arc_set->role();
+ bool is_check = role->isTimingCheckBetween();
+ if (to_bidirect_drvr_vertex && !is_check)
makeEdge(from_vertex, to_bidirect_drvr_vertex, arc_set);
else if (to_vertex) {
makeEdge(from_vertex, to_vertex, arc_set);
@@ -856,7 +854,7 @@ Graph::arcDelayAnnotated(const Edge *edge,
const TimingArc *arc,
DcalcAPIndex ap_index) const
{
- if (arc_delay_annotated_.size()) {
+ if (!arc_delay_annotated_.empty()) {
size_t index = (edge->arcDelays() + arc->index()) * ap_count_ + ap_index;
if (index >= arc_delay_annotated_.size())
report_->critical(1080, "arc_delay_annotated array bounds exceeded");
@@ -912,7 +910,6 @@ Graph::setDelayCount(DcalcAPIndex ap_count)
// Discard any existing delays.
deleteSlewTables();
deleteArcDelayTables();
- removeWidthCheckAnnotations();
removePeriodCheckAnnotations();
makeSlewTables(ap_count);
makeArcDelayTables(ap_count);
@@ -955,11 +952,11 @@ Graph::delayAnnotated(Edge *edge)
TimingArcSet *arc_set = edge->timingArcSet();
for (TimingArc *arc : arc_set->arcs()) {
for (DcalcAPIndex ap_index = 0; ap_index < ap_count_; ap_index++) {
- if (arcDelayAnnotated(edge, arc, ap_index))
- return true;
+ if (!arcDelayAnnotated(edge, arc, ap_index))
+ return false;
}
}
- return false;
+ return true;
}
void
@@ -994,58 +991,28 @@ Graph::makeVertexSlews(Vertex *vertex)
////////////////////////////////////////////////////////////////
void
-Graph::widthCheckAnnotation(const Pin *pin,
- const RiseFall *rf,
- DcalcAPIndex ap_index,
- // Return values.
- float &width,
- bool &exists)
+Graph::minPulseWidthArc(Vertex *vertex,
+ // high = rise, low = fall
+ const RiseFall *hi_low,
+ // Return values.
+ Edge *&edge,
+ TimingArc *&arc)
{
- exists = false;
- if (width_check_annotations_) {
- float *widths = width_check_annotations_->findKey(pin);
- if (widths) {
- int index = ap_index * RiseFall::index_count + rf->index();
- width = widths[index];
- if (width >= 0.0)
- exists = true;
+ VertexOutEdgeIterator edge_iter(vertex, this);
+ while (edge_iter.hasNext()) {
+ edge = edge_iter.next();
+ TimingArcSet *arc_set = edge->timingArcSet();
+ if (arc_set->role() == TimingRole::width()) {
+ for (TimingArc *arc1 : arc_set->arcs()) {
+ if (arc1->fromEdge()->asRiseFall() == hi_low) {
+ arc = arc1;
+ return;
+ }
+ }
}
}
-}
-
-void
-Graph::setWidthCheckAnnotation(const Pin *pin,
- const RiseFall *rf,
- DcalcAPIndex ap_index,
- float width)
-{
- if (width_check_annotations_ == nullptr)
- width_check_annotations_ = new WidthCheckAnnotations;
- float *widths = width_check_annotations_->findKey(pin);
- if (widths == nullptr) {
- int width_count = RiseFall::index_count * ap_count_;
- widths = new float[width_count];
- // Use negative (illegal) width values to indicate unannotated checks.
- for (int i = 0; i < width_count; i++)
- widths[i] = -1;
- (*width_check_annotations_)[pin] = widths;
- }
- int index = ap_index * RiseFall::index_count + rf->index();
- widths[index] = width;
-}
-
-void
-Graph::removeWidthCheckAnnotations()
-{
- if (width_check_annotations_) {
- WidthCheckAnnotations::Iterator check_iter(width_check_annotations_);
- while (check_iter.hasNext()) {
- float *widths = check_iter.next();
- delete [] widths;
- }
- delete width_check_annotations_;
- width_check_annotations_ = nullptr;
- }
+ edge = nullptr;
+ arc = nullptr;
}
////////////////////////////////////////////////////////////////
@@ -1112,7 +1079,6 @@ Graph::removeDelaySlewAnnotations()
}
vertex->removeSlewAnnotated();
}
- removeWidthCheckAnnotations();
removePeriodCheckAnnotations();
}
diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh
index b1dabe55..7336c394 100644
--- a/include/sta/ArcDelayCalc.hh
+++ b/include/sta/ArcDelayCalc.hh
@@ -72,6 +72,7 @@ public:
Edge *edge() const { return edge_; }
const TimingArc *arc() const { return arc_; }
Slew inSlew() const { return in_slew_; }
+ float inSlewFlt() const;
void setInSlew(Slew in_slew);
const Parasitic *parasitic() { return parasitic_; }
void setParasitic(const Parasitic *parasitic);
diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh
index 593e0f2f..8bf525bc 100644
--- a/include/sta/Graph.hh
+++ b/include/sta/Graph.hh
@@ -46,7 +46,6 @@ typedef ArrayTable RequiredsTable;
typedef ArrayTable PrevPathsTable;
typedef Map PinVertexMap;
typedef Iterator VertexEdgeIterator;
-typedef Map WidthCheckAnnotations;
typedef Map PeriodCheckAnnotations;
typedef Vector DelayTableSeq;
typedef ObjectId EdgeId;
@@ -184,18 +183,11 @@ public:
int edgeCount() { return edges_->size(); }
virtual int arcCount() { return arc_count_; }
- // Sdf width check annotation.
- void widthCheckAnnotation(const Pin *pin,
- const RiseFall *rf,
- DcalcAPIndex ap_index,
- // Return values.
- float &width,
- bool &exists);
- void setWidthCheckAnnotation(const Pin *pin,
- const RiseFall *rf,
- DcalcAPIndex ap_index,
- float width);
-
+ void minPulseWidthArc(Vertex *vertex,
+ const RiseFall *hi_low,
+ // Return values.
+ Edge *&edge,
+ TimingArc *&arc);
// Sdf period check annotation.
void periodCheckAnnotation(const Pin *pin,
DcalcAPIndex ap_index,
@@ -229,7 +221,6 @@ protected:
virtual void makePortInstanceEdges(const Instance *inst,
LibertyCell *cell,
LibertyPort *from_to_port);
- void removeWidthCheckAnnotations();
void removePeriodCheckAnnotations();
void makeSlewTables(DcalcAPIndex count);
void deleteSlewTables();
@@ -264,8 +255,6 @@ protected:
DelayTableSeq slew_tables_; // [ap_index][tr_index][vertex_id]
VertexId slew_count_;
DelayTableSeq arc_delays_; // [ap_index][edge_arc_index]
- // Sdf width check annotations.
- WidthCheckAnnotations *width_check_annotations_;
// Sdf period check annotations.
PeriodCheckAnnotations *period_check_annotations_;
// Register/latch clock vertices to search from.
diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh
index bf69f979..5e581065 100644
--- a/include/sta/GraphDelayCalc.hh
+++ b/include/sta/GraphDelayCalc.hh
@@ -109,17 +109,6 @@ public:
// Precedence:
// SDF annotation
// Liberty library
- // (ignores set_min_pulse_width constraint)
- void minPulseWidth(const Pin *pin,
- const RiseFall *hi_low,
- DcalcAPIndex ap_index,
- const MinMax *min_max,
- // Return values.
- float &min_width,
- bool &exists);
- // Precedence:
- // SDF annotation
- // Liberty library
void minPeriod(const Pin *pin,
// Return values.
float &min_period,
diff --git a/include/sta/Liberty.hh b/include/sta/Liberty.hh
index 1d6e16c6..943f43ff 100644
--- a/include/sta/Liberty.hh
+++ b/include/sta/Liberty.hh
@@ -728,17 +728,12 @@ public:
void minPeriod(float &min_period,
bool &exists) const;
void setMinPeriod(float min_period);
+ // This corresponds to the min_pulse_width_high/low port attribute.
// high = rise, low = fall
void minPulseWidth(const RiseFall *hi_low,
- const OperatingConditions *op_cond,
- const Pvt *pvt,
float &min_width,
bool &exists) const;
- // Unscaled value.
- void minPulseWidth(const RiseFall *hi_low,
- float &min_width,
- bool &exists) const;
- void setMinPulseWidth(RiseFall *hi_low,
+ void setMinPulseWidth(const RiseFall *hi_low,
float min_width);
bool isClock() const;
void setIsClock(bool is_clk);
diff --git a/include/sta/PathEnd.hh b/include/sta/PathEnd.hh
index 8b75ccda..ec1cca01 100644
--- a/include/sta/PathEnd.hh
+++ b/include/sta/PathEnd.hh
@@ -143,7 +143,7 @@ public:
// PathEndDataCheck data clock path.
virtual const PathVertex *dataClkPath() const { return nullptr; }
virtual int setupDefaultCycles() const { return 1; }
- virtual float clkSkew(const StaState *sta);
+ virtual Delay clkSkew(const StaState *sta);
static bool less(const PathEnd *path_end1,
const PathEnd *path_end2,
@@ -328,7 +328,7 @@ public:
virtual int exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const;
virtual Delay sourceClkDelay(const StaState *sta) const;
- virtual float clkSkew(const StaState *sta);
+ virtual Delay clkSkew(const StaState *sta);
protected:
PathEndCheck(Path *path,
diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh
index a8cb921e..2c730191 100644
--- a/include/sta/Sdc.hh
+++ b/include/sta/Sdc.hh
@@ -445,6 +445,8 @@ public:
float delay);
void removeClockInsertion(const Clock *clk,
const Pin *pin);
+ static void moveClockInsertions(Sdc *from,
+ Sdc *to);
bool hasClockInsertion(const Pin *pin) const;
float clockInsertion(const Clock *clk,
const RiseFall *rf,
@@ -1066,7 +1068,7 @@ protected:
virtual void recordPathDelayInternalEndpoints(ExceptionPath *exception);
virtual void unrecordPathDelayInternalEndpoints(ExceptionPath *exception);
bool pathDelayTo(const Pin *pin);
- bool hasLibertyChecks(const Pin *pin);
+ bool hasLibertyCheckTo(const Pin *pin);
void deleteMatchingExceptions(ExceptionPath *exception);
void findMatchingExceptions(ExceptionPath *exception,
ExceptionPathSet &matches);
diff --git a/include/sta/TimingRole.hh b/include/sta/TimingRole.hh
index 51cbf6b7..9dc6951b 100644
--- a/include/sta/TimingRole.hh
+++ b/include/sta/TimingRole.hh
@@ -65,6 +65,8 @@ public:
int index() const { return index_; }
bool isWire() const;
bool isTimingCheck() const { return is_timing_check_; }
+ // TIming check but not width or period.
+ bool isTimingCheckBetween() const;
bool isAsyncTimingCheck() const;
bool isNonSeqTimingCheck() const { return is_non_seq_check_; }
bool isDataCheck() const;
diff --git a/liberty/Liberty.cc b/liberty/Liberty.cc
index e486915c..4208f3ee 100644
--- a/liberty/Liberty.cc
+++ b/liberty/Liberty.cc
@@ -1896,7 +1896,7 @@ void
LibertyCell::ensureVoltageWaveforms()
{
if (!have_voltage_waveforms_) {
- float vdd = 0;
+ float vdd = 0.0; // shutup gcc
bool vdd_exists;
liberty_library_->supplyVoltage("VDD", vdd, vdd_exists);
if (!vdd_exists || vdd == 0.0)
@@ -2330,28 +2330,6 @@ LibertyPort::setMinPeriod(float min_period)
min_period_exists_ = true;
}
-void
-LibertyPort::minPulseWidth(const RiseFall *hi_low,
- const OperatingConditions *op_cond,
- const Pvt *pvt,
- float &min_width,
- bool &exists) const
-{
- if (scaled_ports_) {
- LibertyPort *scaled_port = (*scaled_ports_)[op_cond];
- if (scaled_port) {
- scaled_port->minPulseWidth(hi_low, min_width, exists);
- return;
- }
- }
- int hi_low_index = hi_low->index();
- LibertyLibrary *lib = liberty_cell_->libertyLibrary();
- min_width = min_pulse_width_[hi_low_index]
- * lib->scaleFactor(ScaleFactorType::min_pulse_width, hi_low_index,
- liberty_cell_, pvt);
- exists = min_pulse_width_exists_ & (1 << hi_low_index);
-}
-
void
LibertyPort::minPulseWidth(const RiseFall *hi_low,
float &min_width,
@@ -2363,7 +2341,7 @@ LibertyPort::minPulseWidth(const RiseFall *hi_low,
}
void
-LibertyPort::setMinPulseWidth(RiseFall *hi_low,
+LibertyPort::setMinPulseWidth(const RiseFall *hi_low,
float min_width)
{
int hi_low_index = hi_low->index();
diff --git a/liberty/LibertyBuilder.cc b/liberty/LibertyBuilder.cc
index 39a1f1b8..1b353ebf 100644
--- a/liberty/LibertyBuilder.cc
+++ b/liberty/LibertyBuilder.cc
@@ -276,6 +276,8 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell,
return makeClockTreePathArcs(cell, to_port, TimingRole::clockTreePathMax(),
MinMax::max(), attrs);
case TimingType::min_pulse_width:
+ return makeMinPulseWidthArcs(cell, from_port, to_port, related_out,
+ TimingRole::width(), attrs);
case TimingType::minimum_period:
case TimingType::nochange_high_high:
case TimingType::nochange_high_low:
@@ -668,6 +670,26 @@ LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell,
return arc_set;
}
+TimingArcSet *
+LibertyBuilder::makeMinPulseWidthArcs(LibertyCell *cell,
+ LibertyPort *from_port,
+ LibertyPort *to_port,
+ LibertyPort *related_out,
+ TimingRole *role,
+ TimingArcAttrsPtr attrs)
+{
+ TimingArcSet *arc_set = makeTimingArcSet(cell, from_port, to_port, related_out,
+ role, attrs);
+ for (auto to_rf : RiseFall::range()) {
+ TimingModel *model = attrs->model(to_rf);
+ if (model)
+ makeTimingArc(arc_set, to_rf->opposite(), to_rf, model);
+ }
+ return arc_set;
+}
+
+////////////////////////////////////////////////////////////////
+
TimingArcSet *
LibertyBuilder::makeTimingArcSet(LibertyCell *cell,
LibertyPort *from,
diff --git a/liberty/LibertyBuilder.hh b/liberty/LibertyBuilder.hh
index 820112c0..7aed7212 100644
--- a/liberty/LibertyBuilder.hh
+++ b/liberty/LibertyBuilder.hh
@@ -84,6 +84,12 @@ public:
TimingRole *role,
const MinMax *min_max,
TimingArcAttrsPtr attrs);
+ TimingArcSet *makeMinPulseWidthArcs(LibertyCell *cell,
+ LibertyPort *from_port,
+ LibertyPort *to_port,
+ LibertyPort *related_out,
+ TimingRole *role,
+ TimingArcAttrsPtr attrs);
protected:
ConcretePort *makeBusPort(const char *name,
diff --git a/liberty/LibertyReader.cc b/liberty/LibertyReader.cc
index 5d394ea4..ae7cc4b9 100644
--- a/liberty/LibertyReader.cc
+++ b/liberty/LibertyReader.cc
@@ -1922,8 +1922,10 @@ LibertyReader::finishPortGroups()
{
for (PortGroup *port_group : cell_port_groups_) {
int line = port_group->line();
- for (LibertyPort *port : *port_group->ports())
+ for (LibertyPort *port : *port_group->ports()) {
checkPort(port, line);
+ makeMinPulseWidthArcs(port, line);
+ }
makeTimingArcs(port_group);
makeInternalPowers(port_group);
delete port_group;
@@ -1947,6 +1949,47 @@ LibertyReader::checkPort(LibertyPort *port,
port->setDirection(PortDirection::tristate());
}
+// Make timing arcs for the port min_pulse_width_low/high attributes.
+// This is redundant but makes sdf annotation consistent.
+void
+LibertyReader::makeMinPulseWidthArcs(LibertyPort *port,
+ int line)
+{
+ TimingArcAttrsPtr attrs = nullptr;
+ for (auto hi_low : RiseFall::range()) {
+ float min_width;
+ bool exists;
+ port->minPulseWidth(hi_low, min_width, exists);
+ if (exists) {
+ if (attrs == nullptr) {
+ attrs = make_shared();
+ attrs->setTimingType(TimingType::min_pulse_width);
+ }
+ // rise/fall_constraint model is on the trailing edge of the pulse.
+ const RiseFall *model_rf = hi_low->opposite();
+ TimingModel *check_model =
+ makeScalarCheckModel(min_width, ScaleFactorType::min_pulse_width, model_rf);
+ attrs->setModel(model_rf, check_model);
+ }
+ }
+ if (attrs)
+ builder_.makeTimingArcs(cell_, port, port, nullptr, attrs, line);
+}
+
+TimingModel *
+LibertyReader::makeScalarCheckModel(float value,
+ ScaleFactorType scale_factor_type,
+ const RiseFall *rf)
+{
+ TablePtr table = make_shared(value);
+ TableTemplate *tbl_template =
+ library_->findTableTemplate("scalar", TableTemplateType::delay);
+ TableModel *table_model = new TableModel(table, tbl_template,
+ scale_factor_type, rf);
+ CheckTableModel *check_model = new CheckTableModel(cell_, table_model, nullptr);
+ return check_model;
+}
+
void
LibertyReader::makeTimingArcs(PortGroup *port_group)
{
@@ -3530,7 +3573,7 @@ LibertyReader::visitMinPulseWidthHigh(LibertyAttr *attr)
void
LibertyReader::visitMinPulseWidth(LibertyAttr *attr,
- RiseFall *rf)
+ const RiseFall *rf)
{
if (cell_) {
float value;
diff --git a/liberty/LibertyReaderPvt.hh b/liberty/LibertyReaderPvt.hh
index 6a088619..6ef9365f 100644
--- a/liberty/LibertyReaderPvt.hh
+++ b/liberty/LibertyReaderPvt.hh
@@ -235,7 +235,7 @@ public:
virtual void visitMinPulseWidthLow(LibertyAttr *attr);
virtual void visitMinPulseWidthHigh(LibertyAttr *attr);
virtual void visitMinPulseWidth(LibertyAttr *attr,
- RiseFall *rf);
+ const RiseFall *rf);
virtual void visitPulseClock(LibertyAttr *attr);
virtual void visitClockGateClockPin(LibertyAttr *attr);
virtual void visitClockGateEnablePin(LibertyAttr *attr);
@@ -479,6 +479,11 @@ public:
virtual void visitAttr9(LibertyAttr *) {}
protected:
+ TimingModel *makeScalarCheckModel(float value,
+ ScaleFactorType scale_factor_type,
+ const RiseFall *rf);
+ void makeMinPulseWidthArcs(LibertyPort *port,
+ int line);
void setEnergyScale();
void defineVisitors();
virtual void begin(LibertyGroup *group);
diff --git a/liberty/LibertyWriter.cc b/liberty/LibertyWriter.cc
index 2a53babf..7d01985f 100644
--- a/liberty/LibertyWriter.cc
+++ b/liberty/LibertyWriter.cc
@@ -55,17 +55,21 @@ protected:
void writePortAttrs(const LibertyPort *port);
void writeTimingArcSet(const TimingArcSet *arc_set);
void writeTimingModels(const TimingArc *arc,
- RiseFall *rf);
+ const RiseFall *rf);
void writeTableModel(const TableModel *model);
void writeTableModel0(const TableModel *model);
void writeTableModel1(const TableModel *model);
void writeTableModel2(const TableModel *model);
- void writeTableAxis(const TableAxis *axis,
- int index);
+ void writeTableAxis4(const TableAxis *axis,
+ int index);
+ void writeTableAxis10(const TableAxis *axis,
+ int index);
const char *asString(bool value);
const char *asString(const PortDirection *dir);
const char *timingTypeString(const TimingArcSet *arc_set);
+ bool isAutoWidthArc(const LibertyPort *port,
+ const TimingArcSet *arc_set);
const LibertyLibrary *library_;
const char *filename_;
@@ -214,20 +218,21 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
fprintf(stream_, " variable_3 : %s;\n",
tableVariableString(axis3->variable()));
if (axis1)
- writeTableAxis(axis1, 1);
+ writeTableAxis4(axis1, 1);
if (axis2)
- writeTableAxis(axis2, 2);
+ writeTableAxis4(axis2, 2);
if (axis3)
- writeTableAxis(axis3, 3);
+ writeTableAxis4(axis3, 3);
fprintf(stream_, " }\n");
}
}
+// indent 4
void
-LibertyWriter::writeTableAxis(const TableAxis *axis,
- int index)
+LibertyWriter::writeTableAxis4(const TableAxis *axis,
+ int index)
{
- fprintf(stream_, " index_%d (\"", index);
+ fprintf(stream_, " index_%d(\"", index);
const Unit *unit = tableVariableUnit(axis->variable(), library_->units());
bool first = true;
for (size_t i = 0; i < axis->size(); i++) {
@@ -239,6 +244,15 @@ LibertyWriter::writeTableAxis(const TableAxis *axis,
fprintf(stream_, "\");\n");
}
+// indent 10
+void
+LibertyWriter::writeTableAxis10(const TableAxis *axis,
+ int index)
+{
+ fprintf(stream_, " ");
+ writeTableAxis4(axis, index);
+}
+
void
LibertyWriter::writeBusDcls()
{
@@ -356,8 +370,25 @@ LibertyWriter::writePortAttrs(const LibertyPort *port)
fprintf(stream_, " max_capacitance : %s;\n",
cap_unit_->asString(limit, 3));
- for (TimingArcSet *arc_set : port->libertyCell()->timingArcSets(nullptr,port))
- writeTimingArcSet(arc_set);
+ for (TimingArcSet *arc_set : port->libertyCell()->timingArcSets(nullptr,port)) {
+ if (!isAutoWidthArc(port, arc_set))
+ writeTimingArcSet(arc_set);
+ }
+}
+
+// Check if arc is added for port min_pulse_width_high/low attribute.
+bool
+LibertyWriter::isAutoWidthArc(const LibertyPort *port,
+ const TimingArcSet *arc_set)
+{
+ if (arc_set->role() == TimingRole::width()) {
+ float min_width;
+ bool exists1, exists2;
+ port->minPulseWidth(RiseFall::rise(), min_width, exists1);
+ port->minPulseWidth(RiseFall::fall(), min_width, exists2);
+ return exists1 || exists2;
+ }
+ return false;
}
void
@@ -386,7 +417,7 @@ LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
void
LibertyWriter::writeTimingModels(const TimingArc *arc,
- RiseFall *rf)
+ const RiseFall *rf)
{
TimingModel *model = arc->model();
const GateTableModel *gate_model = dynamic_cast(model);
@@ -450,6 +481,7 @@ LibertyWriter::writeTableModel0(const TableModel *model)
void
LibertyWriter::writeTableModel1(const TableModel *model)
{
+ writeTableAxis10(model->axis1(), 1);
fprintf(stream_, " values(\"");
bool first_col = true;
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
@@ -465,6 +497,8 @@ LibertyWriter::writeTableModel1(const TableModel *model)
void
LibertyWriter::writeTableModel2(const TableModel *model)
{
+ writeTableAxis10(model->axis1(), 1);
+ writeTableAxis10(model->axis2(), 2);
fprintf(stream_, " values(\"");
bool first_row = true;
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
@@ -577,6 +611,8 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
return "min_clock_tree_path";
else if (role == TimingRole::clockTreePathMax())
return "max_clock_tree_path";
+ else if (role == TimingRole::width())
+ return "min_pulse_width";
else {
report_->error(1333, "%s/%s/%s timing arc type %s not supported.",
library_->name(),
diff --git a/liberty/TimingRole.cc b/liberty/TimingRole.cc
index 221f7305..deb4f762 100644
--- a/liberty/TimingRole.cc
+++ b/liberty/TimingRole.cc
@@ -258,6 +258,14 @@ TimingRole::isLatchDtoQ() const
return this == latch_d_q_;
}
+bool
+TimingRole::isTimingCheckBetween() const
+{
+ return is_timing_check_
+ && this != width_
+ && this != period_;
+}
+
bool
TimingRole::less(const TimingRole *role1,
const TimingRole *role2)
diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc
index aa728ac5..c78edd4d 100644
--- a/sdc/Sdc.cc
+++ b/sdc/Sdc.cc
@@ -668,17 +668,9 @@ Sdc::moveDeratingFactors(Sdc *from,
from->derating_factors_ = nullptr;
}
- to->net_derating_factors_.deleteContents();
- to->net_derating_factors_ = from->net_derating_factors_;
- from->net_derating_factors_.clear();
-
- to->inst_derating_factors_.deleteContents();
- to->inst_derating_factors_ = from->inst_derating_factors_;
- from->inst_derating_factors_.clear();
-
- to->cell_derating_factors_.deleteContents();
- to->cell_derating_factors_ = from->cell_derating_factors_;
- from->cell_derating_factors_.clear();
+ to->net_derating_factors_ = std::move(from->net_derating_factors_);
+ to->inst_derating_factors_ = std::move(from->inst_derating_factors_);
+ to->cell_derating_factors_ = std::move(from->cell_derating_factors_);
}
void
@@ -1740,6 +1732,13 @@ Sdc::removeClockInsertion(const Clock *clk,
deleteClockInsertion(insertion);
}
+void
+Sdc::moveClockInsertions(Sdc *from,
+ Sdc *to)
+{
+ to->clk_insertions_ = std::move(from->clk_insertions_);
+}
+
void
Sdc::deleteClockInsertion(ClockInsertion *insertion)
{
@@ -2746,46 +2745,18 @@ void
Sdc::movePortDelays(Sdc *from,
Sdc *to)
{
- to->input_delays_.deleteContents();
- to->input_delays_ = from->input_delays_;
- from->input_delays_.clear();
-
- to->input_delay_pin_map_.deleteContents();
- to->input_delay_pin_map_ = from->input_delay_pin_map_;
- from->input_delay_pin_map_.clear();
-
- to->input_delay_ref_pin_map_.deleteContents();
- to->input_delay_ref_pin_map_ = from->input_delay_ref_pin_map_;
- from->input_delay_ref_pin_map_.clear();
-
- to->input_delay_leaf_pin_map_.deleteContents();
- to->input_delay_leaf_pin_map_ = from->input_delay_leaf_pin_map_;
- from->input_delay_leaf_pin_map_.clear();
-
- to->input_delay_internal_pin_map_.deleteContents();
- to->input_delay_internal_pin_map_ = from->input_delay_internal_pin_map_;
- from->input_delay_internal_pin_map_.clear();
-
+ to->input_delays_ = std::move(from->input_delays_);
+ to->input_delay_pin_map_ = std::move(from->input_delay_pin_map_);
+ to->input_delay_ref_pin_map_ = std::move(from->input_delay_ref_pin_map_);
+ to->input_delay_leaf_pin_map_ = std::move(from->input_delay_leaf_pin_map_);
+ to->input_delay_internal_pin_map_ = std::move(from->input_delay_internal_pin_map_);
to->input_delay_index_ = from->input_delay_index_;
from->input_delay_index_ = 0;
- ////////////////
-
- to->output_delays_.deleteContents();
- to->output_delays_ = from->output_delays_;
- from->output_delays_.clear();
-
- to->output_delay_pin_map_.deleteContents();
- to->output_delay_pin_map_ = from->output_delay_pin_map_;
- from->output_delay_pin_map_.clear();
-
- to->output_delay_ref_pin_map_.deleteContents();
- to->output_delay_ref_pin_map_ = from->output_delay_ref_pin_map_;
- from->output_delay_ref_pin_map_.clear();
-
- to->output_delay_leaf_pin_map_.deleteContents();
- to->output_delay_leaf_pin_map_ = from->output_delay_leaf_pin_map_;
- from->output_delay_leaf_pin_map_.clear();
+ to->output_delays_ = std::move(from->output_delays_);
+ to->output_delay_pin_map_ = std::move(from->output_delay_pin_map_);
+ to->output_delay_ref_pin_map_ = std::move(from->output_delay_ref_pin_map_);
+ to->output_delay_leaf_pin_map_ = std::move(from->output_delay_leaf_pin_map_);
}
////////////////////////////////////////////////////////////////
@@ -3355,11 +3326,11 @@ Sdc::movePortExtCaps(Sdc *from,
Sdc *to)
{
for (int corner_index = 0; corner_index < from->corners()->count(); corner_index++) {
- to->port_ext_cap_maps_[corner_index] = from->port_ext_cap_maps_[corner_index];
- from->port_ext_cap_maps_[corner_index].clear();
+ to->port_ext_cap_maps_[corner_index] =
+ std::move(from->port_ext_cap_maps_[corner_index]);
- to->net_wire_cap_maps_[corner_index] = from->net_wire_cap_maps_[corner_index];
- from->net_wire_cap_maps_[corner_index].clear();
+ to->net_wire_cap_maps_[corner_index] =
+ std::move(from->net_wire_cap_maps_[corner_index]);
}
}
@@ -3953,7 +3924,7 @@ Sdc::recordPathDelayInternalEndpoints(ExceptionPath *exception)
if (to
&& to->hasPins()) {
for (const Pin *pin : *to->pins()) {
- if (!(hasLibertyChecks(pin)
+ if (!(hasLibertyCheckTo(pin)
|| network_->isTopLevelPort(pin))) {
path_delay_internal_endpoints_.insert(pin);
}
@@ -3969,7 +3940,7 @@ Sdc::unrecordPathDelayInternalEndpoints(ExceptionPath *exception)
&& to->hasPins()
&& !path_delay_internal_endpoints_.empty()) {
for (const Pin *pin : *to->pins()) {
- if (!(hasLibertyChecks(pin)
+ if (!(hasLibertyCheckTo(pin)
|| network_->isTopLevelPort(pin))
&& !pathDelayTo(pin))
path_delay_internal_endpoints_.erase(pin);
@@ -3978,7 +3949,7 @@ Sdc::unrecordPathDelayInternalEndpoints(ExceptionPath *exception)
}
bool
-Sdc::hasLibertyChecks(const Pin *pin)
+Sdc::hasLibertyCheckTo(const Pin *pin)
{
const Instance *inst = network_->instance(pin);
LibertyCell *cell = network_->libertyCell(inst);
@@ -3986,7 +3957,7 @@ Sdc::hasLibertyChecks(const Pin *pin)
LibertyPort *port = network_->libertyPort(pin);
if (port) {
for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) {
- if (arc_set->role()->isTimingCheck())
+ if (arc_set->role()->isTimingCheckBetween())
return true;
}
}
diff --git a/sdf/ReportAnnotation.cc b/sdf/ReportAnnotation.cc
index f51858ba..1bfdc077 100644
--- a/sdf/ReportAnnotation.cc
+++ b/sdf/ReportAnnotation.cc
@@ -68,7 +68,7 @@ protected:
void init();
void findCounts();
- void findWidthPeriodCount(Pin *pin);
+ void findPeriodCount(Pin *pin);
void reportDelayCounts();
void reportCheckCounts();
void reportArcs();
@@ -78,9 +78,9 @@ protected:
void reportArcs(Vertex *vertex,
bool report_annotated,
int &i);
- void reportWidthPeriodArcs(const Pin *pin,
- bool report_annotated,
- int &i);
+ void reportPeriodArcs(const Pin *pin,
+ bool report_annotated,
+ int &i);
void reportCount(const char *title,
int index,
int &total,
@@ -282,9 +282,8 @@ ReportAnnotated::reportCheckCount(TimingRole *role,
{
int index = role->index();
if (edge_count_[index] > 0) {
- const char *role_name = role->asString();
string title;
- stringPrint(title, "cell %s arcs", role_name);
+ stringPrint(title, "cell %s arcs", role->asString());
reportCount(title.c_str(), index, total, annotated_total);
}
}
@@ -345,7 +344,7 @@ ReportAnnotated::findCounts()
}
}
}
- findWidthPeriodCount(from_pin);
+ findPeriodCount(from_pin);
}
}
@@ -376,7 +375,7 @@ ReportAnnotated::roleIndex(const TimingRole *role,
// Width and period checks are not edges in the graph so
// they require special handling.
void
-ReportAnnotated::findWidthPeriodCount(Pin *pin)
+ReportAnnotated::findPeriodCount(Pin *pin)
{
LibertyPort *port = network_->libertyPort(pin);
if (port) {
@@ -400,27 +399,6 @@ ReportAnnotated::findWidthPeriodCount(Pin *pin)
}
}
}
-
- int width_index = TimingRole::width()->index();
- if (report_role_[width_index]) {
- for (auto hi_low : RiseFall::range()) {
- port->minPulseWidth(hi_low, value, exists);
- if (exists) {
- edge_count_[width_index]++;
- graph_->widthCheckAnnotation(pin, hi_low, ap_index,
- value, annotated);
- if (annotated) {
- edge_annotated_count_[width_index]++;
- if (list_annotated_)
- annotated_pins_.insert(pin);
- }
- else {
- if (list_unannotated_)
- unannotated_pins_.insert(pin);
- }
- }
- }
- }
}
}
@@ -476,7 +454,7 @@ ReportAnnotated::reportArcs(const char *header,
reportArcs(vertex, report_annotated, i);
if (bidirect_drvr_vertex)
reportArcs(bidirect_drvr_vertex, report_annotated, i);
- reportWidthPeriodArcs(pin, report_annotated, i);
+ reportPeriodArcs(pin, report_annotated, i);
if (max_lines_ != 0 && i > max_lines_)
break;
}
@@ -521,15 +499,13 @@ ReportAnnotated::reportArcs(Vertex *vertex,
}
void
-ReportAnnotated::reportWidthPeriodArcs(const Pin *pin,
- bool report_annotated,
- int &i)
+ReportAnnotated::reportPeriodArcs(const Pin *pin,
+ bool report_annotated,
+ int &i)
{
LibertyPort *port = network_->libertyPort(pin);
if (port) {
DcalcAPIndex ap_index = 0;
- float value;
- bool exists, annotated;
int period_index = TimingRole::period()->index();
if (report_role_[period_index]
&& (max_lines_ == 0 || i < max_lines_)) {
@@ -547,27 +523,6 @@ ReportAnnotated::reportWidthPeriodArcs(const Pin *pin,
}
}
}
-
- int width_index = TimingRole::width()->index();
- if (report_role_[width_index]
- && (max_lines_ == 0 || i < max_lines_)) {
- bool report = false;
- for (auto hi_low : RiseFall::range()) {
- port->minPulseWidth(hi_low, value, exists);
- if (exists) {
- edge_count_[width_index]++;
- graph_->widthCheckAnnotation(pin, hi_low, ap_index,
- value, annotated);
- report |= (annotated == report_annotated);
- }
- }
- if (report) {
- report_->reportLine(" %-18s %s",
- "min width",
- network_->pathName(pin));
- i++;
- }
- }
}
}
diff --git a/sdf/SdfReader.cc b/sdf/SdfReader.cc
index 7dff5edd..195f51be 100644
--- a/sdf/SdfReader.cc
+++ b/sdf/SdfReader.cc
@@ -550,22 +550,12 @@ SdfReader::timingCheckWidth(SdfPortSpec *edge,
if (port) {
Pin *pin = network_->findPin(instance_, port_name);
if (pin) {
- const RiseFall *rf = edge->transition()->asRiseFall();
- float **values = triple->values();
- float *value_ptr = values[triple_min_index_];
- if (value_ptr) {
- float value = *value_ptr;
- graph_->setWidthCheckAnnotation(pin, rf, arc_delay_min_index_,
- value);
- }
- if (triple_max_index_ != null_index_) {
- value_ptr = values[triple_max_index_];
- if (value_ptr) {
- float value = *value_ptr;
- graph_->setWidthCheckAnnotation(pin, rf, arc_delay_max_index_,
- value);
- }
- }
+ const RiseFall *rf = edge->transition()->asRiseFall();
+ Edge *edge;
+ TimingArc *arc;
+ graph_->minPulseWidthArc(graph_->pinLoadVertex(pin), rf, edge, arc);
+ if (edge)
+ setEdgeArcDelays(edge, arc, triple);
}
}
}
diff --git a/sdf/SdfWriter.cc b/sdf/SdfWriter.cc
index 1fa32bab..22d2e24e 100644
--- a/sdf/SdfWriter.cc
+++ b/sdf/SdfWriter.cc
@@ -522,14 +522,12 @@ SdfWriter::writeTimingChecks(const Instance *inst,
}
for (auto hi_low : RiseFall::range()) {
float min_width, max_width;
- bool exists;
- graph_delay_calc_->minPulseWidth(pin, hi_low, arc_delay_min_index_,
- MinMax::min(),
- min_width, exists);
- graph_delay_calc_->minPulseWidth(pin, hi_low, arc_delay_max_index_,
- MinMax::max(),
- max_width, exists);
- if (exists) {
+ Edge *edge;
+ TimingArc *arc;
+ graph_->minPulseWidthArc(vertex, hi_low, edge, arc);
+ if (edge) {
+ min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_));
+ max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_));
ensureTimingCheckheaders(check_header, inst, inst_header);
writeWidthCheck(pin, hi_low, min_width, max_width);
}
diff --git a/search/Bfs.cc b/search/Bfs.cc
index 7971b975..bc77bb4e 100644
--- a/search/Bfs.cc
+++ b/search/Bfs.cc
@@ -87,10 +87,10 @@ BfsIterator::reportEntries(const Network *network)
while (levelLessOrEqual(level, last_level_)) {
VertexSeq &level_vertices = queue_[level];
if (!level_vertices.empty()) {
- printf("Level %d\n", level);
+ report_->reportLine("Level %d", level);
for (auto vertex : level_vertices) {
if (vertex)
- printf(" %s\n", vertex->name(network));
+ report_->reportLine(" %s", vertex->name(network));
}
}
incrLevel(level);
diff --git a/search/CheckMinPulseWidths.cc b/search/CheckMinPulseWidths.cc
index ece0222f..6c1e35a4 100644
--- a/search/CheckMinPulseWidths.cc
+++ b/search/CheckMinPulseWidths.cc
@@ -424,9 +424,11 @@ MinPulseWidthCheck::minWidth(const StaState *sta) const
}
// Precedence:
-// set_min_pulse_width constraint
+// set_min_pulse_width SDC command
// SDF annotation
// Liberty library
+// port min_pulse_width_low/high
+// min_pulse_width timing group
static void
minPulseWidth(const Path *path,
const StaState *sta,
@@ -441,12 +443,17 @@ minPulseWidth(const Path *path,
// set_min_pulse_width command.
sdc->minPulseWidth(pin, clk, rf, min_width, exists);
if (!exists) {
- GraphDelayCalc *graph_dcalc = sta->graphDelayCalc();
- const MinMax *min_max = path->minMax(sta);
const PathAnalysisPt *path_ap = path->pathAnalysisPt(sta);
const DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt();
- graph_dcalc->minPulseWidth(pin, rf, dcalc_ap->index(), min_max,
- min_width, exists);
+ Vertex *vertex = path->vertex(sta);
+ Graph *graph = sta->graph();
+ Edge *edge;
+ TimingArc *arc;
+ graph->minPulseWidthArc(vertex, rf, edge, arc);
+ if (edge) {
+ min_width = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap->index()));
+ exists = true;
+ }
}
}
diff --git a/search/ClkDelays.hh b/search/ClkDelays.hh
index 003b705c..729deb66 100644
--- a/search/ClkDelays.hh
+++ b/search/ClkDelays.hh
@@ -31,19 +31,19 @@ public:
const RiseFall *end_rf,
const MinMax *min_max,
// Return values.
- float &insertion,
- float &delay,
+ Delay &insertion,
+ Delay &delay,
float &lib_clk_delay,
- float &latency,
+ Delay &latency,
PathVertex &path,
bool &exists) const;
void latency(const RiseFall *src_rf,
const RiseFall *end_rf,
const MinMax *min_max,
// Return values.
- float &delay,
+ Delay &delay,
bool &exists) const;
- static float latency(PathVertex *clk_path,
+ static Delay latency(PathVertex *clk_path,
StaState *sta);
void setLatency(const RiseFall *src_rf,
const RiseFall *end_rf,
@@ -59,10 +59,10 @@ private:
static float clkTreeDelay(PathVertex *clk_path,
StaState *sta);
- float insertion_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
- float delay_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
+ Delay insertion_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
+ Delay delay_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
float lib_clk_delay_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
- float latency_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
+ Delay latency_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
PathVertex path_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
bool exists_[RiseFall::index_count][RiseFall::index_count][MinMax::index_count];
};
diff --git a/search/ClkLatency.cc b/search/ClkLatency.cc
index 8c9c90ce..818251a2 100644
--- a/search/ClkLatency.cc
+++ b/search/ClkLatency.cc
@@ -78,19 +78,19 @@ ClkLatency::reportClkLatency(const Clock *clk,
for (const RiseFall *src_rf : RiseFall::range()) {
for (const RiseFall *end_rf : RiseFall::range()) {
PathVertex path_min;
- float insertion_min;
- float delay_min;
+ Delay insertion_min;
+ Delay delay_min;
float lib_clk_delay_min;
- float latency_min;
+ Delay latency_min;
bool exists_min;
clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min,
delay_min, lib_clk_delay_min, latency_min,
path_min, exists_min);
PathVertex path_max;
- float insertion_max;
- float delay_max;
+ Delay insertion_max;
+ Delay delay_max;
float lib_clk_delay_max;
- float latency_max;
+ Delay latency_max;
bool exists_max;
clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max,
delay_max, lib_clk_delay_max, latency_max,
@@ -102,15 +102,15 @@ ClkLatency::reportClkLatency(const Clock *clk,
report_->reportLine(" min max");
report_->reportLine("%7s %7s source latency",
- time_unit->asString(insertion_min, digits),
- time_unit->asString(insertion_max, digits));
+ delayAsString(insertion_min, this, digits),
+ delayAsString(insertion_max, this, digits));
report_->reportLine("%7s %7s network latency %s",
- time_unit->asString(delay_min, digits),
+ delayAsString(delay_min, this, digits),
"",
sdc_network_->pathName(path_min.pin(this)));
report_->reportLine("%7s %7s network latency %s",
"",
- time_unit->asString(delay_max, digits),
+ delayAsString(delay_max, this, digits),
sdc_network_->pathName(path_max.pin(this)));
if (lib_clk_delay_min != 0.0
|| lib_clk_delay_max != 0.0)
@@ -119,11 +119,11 @@ ClkLatency::reportClkLatency(const Clock *clk,
time_unit->asString(lib_clk_delay_max, digits));
report_->reportLine("---------------");
report_->reportLine("%7s %7s latency",
- time_unit->asString(latency_min, digits),
- time_unit->asString(latency_max, digits));
- float skew = latency_max - latency_min;
+ delayAsString(latency_min, this, digits),
+ delayAsString(latency_max, this, digits));
+ Delay skew = latency_max - latency_min;
report_->reportLine(" %7s skew",
- time_unit->asString(skew, digits));
+ delayAsString(skew, this, digits));
report_->reportBlankLine();
}
}
@@ -154,11 +154,11 @@ ClkLatency::findClkDelays(ConstClockSeq clks,
const RiseFall *clk_rf = path_clk_edge->transition();
const MinMax *min_max = path->minMax(this);
const RiseFall *end_rf = path->transition(this);
- float latency = ClkDelays::latency(path, this);
- float clk_latency;
+ Delay latency = ClkDelays::latency(path, this);
+ Delay clk_latency;
bool exists;
clk_delays.latency(clk_rf, end_rf, min_max, clk_latency, exists);
- if (!exists || min_max->compare(latency, clk_latency))
+ if (!exists || delayGreater(latency, clk_latency, min_max, this))
clk_delays.setLatency(clk_rf, end_rf, min_max, path, this);
}
}
@@ -189,10 +189,10 @@ ClkDelays::delay(const RiseFall *src_rf,
const RiseFall *end_rf,
const MinMax *min_max,
// Return values.
- float &insertion,
- float &delay,
+ Delay &insertion,
+ Delay &delay,
float &lib_clk_delay,
- float &latency,
+ Delay &latency,
PathVertex &path,
bool &exists) const
{
@@ -212,7 +212,7 @@ ClkDelays::latency(const RiseFall *src_rf,
const RiseFall *end_rf,
const MinMax *min_max,
// Return values.
- float &latency,
+ Delay &latency,
bool &exists) const
{
int src_rf_index = src_rf->index();
@@ -249,7 +249,7 @@ ClkDelays::setLatency(const RiseFall *src_rf,
exists_[src_rf_index][end_rf_index][mm_index] = true;
}
-float
+Delay
ClkDelays::latency(PathVertex *clk_path,
StaState *sta)
{
@@ -280,7 +280,8 @@ ClkDelays::insertionDelay(PathVertex *clk_path,
const Pin *src_pin = clk_info->clkSrc();
const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(sta);
const MinMax *min_max = clk_path->minMax(sta);
- return sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max, min_max, path_ap);
+ return delayAsFloat(sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max,
+ min_max, path_ap));
}
float
@@ -292,7 +293,7 @@ ClkDelays::clkTreeDelay(PathVertex *clk_path,
const LibertyPort *port = sta->network()->libertyPort(pin);
const MinMax *min_max = clk_path->minMax(sta);
const RiseFall *rf = clk_path->transition(sta);
- Slew slew = clk_path->slew(sta);
+ float slew = delayAsFloat(clk_path->slew(sta));
return port->clkTreeDelay(slew, rf, min_max);
}
diff --git a/search/ClkSkew.cc b/search/ClkSkew.cc
index b3fd5160..6a515d78 100644
--- a/search/ClkSkew.cc
+++ b/search/ClkSkew.cc
@@ -133,7 +133,7 @@ ClkSkew::clkTreeDelay(PathVertex &clk_path,
const LibertyPort *port = sta->network()->libertyPort(pin);
const MinMax *min_max = clk_path.minMax(sta);
const RiseFall *rf = clk_path.transition(sta);
- Slew slew = clk_path.slew(sta);
+ float slew = delayAsFloat(clk_path.slew(sta));
return port->clkTreeDelay(slew, rf, min_max);
}
diff --git a/search/FindRegister.cc b/search/FindRegister.cc
index 547474a4..90973487 100644
--- a/search/FindRegister.cc
+++ b/search/FindRegister.cc
@@ -530,11 +530,13 @@ FindRegClkPins::FindRegClkPins(StaState *sta) :
bool
FindRegClkPins::matchPin(Pin *pin)
{
+ // Liberty port clock attribute is not present in latches (for nlc18 anyway).
LibertyPort *port = network_->libertyPort(pin);
LibertyCell *cell = port->libertyCell();
for (TimingArcSet *arc_set : cell->timingArcSets(port, nullptr)) {
TimingRole *role = arc_set->role();
- if (role->isTimingCheck())
+ if (role == TimingRole::regClkToQ()
+ || role == TimingRole::latchEnToQ())
return true;
}
return false;
diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc
index bacdaeea..10867949 100644
--- a/search/MakeTimingModel.cc
+++ b/search/MakeTimingModel.cc
@@ -114,6 +114,7 @@ MakeTimingModel::saveSdc()
Sdc::movePortDelays(sdc_, sdc_backup_);
Sdc::movePortExtCaps(sdc_, sdc_backup_);
Sdc::moveDeratingFactors(sdc_, sdc_backup_);
+ Sdc::moveClockInsertions(sdc_, sdc_backup_);
sta_->delaysInvalid();
}
@@ -123,6 +124,7 @@ MakeTimingModel::restoreSdc()
Sdc::movePortDelays(sdc_backup_, sdc_);
Sdc::movePortExtCaps(sdc_backup_, sdc_);
Sdc::moveDeratingFactors(sdc_backup_, sdc_);
+ Sdc::moveClockInsertions(sdc_backup_, sdc_);
delete sdc_backup_;
sta_->delaysInvalid();
}
@@ -568,7 +570,8 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port,
? clk_rf
: clk_rf->opposite();
PathVertex clk_path;
- float insertion, delay, lib_clk_delay, latency;
+ Delay insertion, delay, latency;
+ float lib_clk_delay;
bool exists;
delays.delay(clk_rf, end_rf, min_max, insertion, delay,
lib_clk_delay, latency, clk_path, exists);
diff --git a/search/PathEnd.cc b/search/PathEnd.cc
index 02098a63..42b27305 100644
--- a/search/PathEnd.cc
+++ b/search/PathEnd.cc
@@ -1069,7 +1069,7 @@ PathEndCheck::sourceClkDelay(const StaState *sta) const
// Propagated clock. Propagated arrival is seeded with insertion delay.
Arrival clk_arrival = src_clk_path.arrival(sta);
const ClockEdge *src_clk_edge = src_clk_info->clkEdge();
- float insertion = sourceClkInsertionDelay(sta);
+ Delay insertion = sourceClkInsertionDelay(sta);
return delayRemove(clk_arrival - src_clk_edge->time(), insertion);
}
else
@@ -1080,7 +1080,7 @@ PathEndCheck::sourceClkDelay(const StaState *sta) const
return 0.0;
}
-float
+Delay
PathEndCheck::clkSkew(const StaState *sta)
{
commonClkPessimism(sta);
@@ -1858,7 +1858,7 @@ PathEndPathDelay::sourceClkOffset(const StaState *sta) const
return pathDelaySrcClkOffset(path_, path_delay_, src_clk_arrival_, sta);
}
-float
+Delay
PathEnd::clkSkew(const StaState *)
{
return 0.0;
diff --git a/search/Sim.cc b/search/Sim.cc
index 2e37b334..6c46dabe 100644
--- a/search/Sim.cc
+++ b/search/Sim.cc
@@ -142,7 +142,6 @@ Sim::funcBddSim(const FuncExpr *expr,
if (port_node) {
LogicValue value = logicValue(pin);
int var_index = Cudd_NodeReadIndex(port_node);
- //printf("%s %d %c\n", port->name(), var_index, logicValueString(value));
switch (value) {
case LogicValue::zero:
bdd = Cudd_bddCompose(cudd_mgr, bdd, Cudd_ReadLogicZero(cudd_mgr), var_index);
diff --git a/search/Sta.cc b/search/Sta.cc
index f0c6500c..01a6766d 100644
--- a/search/Sta.cc
+++ b/search/Sta.cc
@@ -5167,7 +5167,9 @@ instMaxSlew(const Instance *inst,
Pin *pin = pin_iter->next();
if (network->isDriver(pin)) {
Vertex *vertex = graph->pinDrvrVertex(pin);
- max_slew = max(max_slew, sta->vertexSlew(vertex, MinMax::max()));
+ Slew slew = sta->vertexSlew(vertex, MinMax::max());
+ if (delayGreater(slew, max_slew, sta))
+ max_slew = slew;
}
}
delete pin_iter;
diff --git a/tcl/Sdc.tcl b/tcl/Sdc.tcl
index 322d1e62..a916b6a8 100644
--- a/tcl/Sdc.tcl
+++ b/tcl/Sdc.tcl
@@ -965,8 +965,8 @@ proc get_ports { args } {
}
variable filter_regexp1 {@?([a-zA-Z_]+) *(==|!=|=~) *([0-9a-zA-Z_\*]+)}
-variable filter_or_regexp "($filter_regexp1) +\\|\\| +($filter_regexp1)"
-variable filter_and_regexp "($filter_regexp1) +&& +($filter_regexp1)"
+variable filter_or_regexp "($filter_regexp1) *\\|\\| *($filter_regexp1)"
+variable filter_and_regexp "($filter_regexp1) *&& *($filter_regexp1)"
proc filter_ports1 { filter objects } {
variable filter_regexp1
diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i
index 750a4989..1d45e6e1 100644
--- a/tcl/StaTcl.i
+++ b/tcl/StaTcl.i
@@ -4940,7 +4940,7 @@ bool path_delay_margin_is_external()
Crpr common_clk_pessimism() { return self->commonClkPessimism(Sta::sta()); }
RiseFall *target_clk_end_trans()
{ return const_cast(self->targetClkEndTrans(Sta::sta())); }
-float clk_skew() { return self->clkSkew(Sta::sta()); }
+Delay clk_skew() { return self->clkSkew(Sta::sta()); }
}
diff --git a/test/asap7_invbuf.lib.gz b/test/asap7_invbuf.lib.gz
new file mode 100644
index 00000000..91e03475
Binary files /dev/null and b/test/asap7_invbuf.lib.gz differ
diff --git a/test/ccs_sim1.ok b/test/ccs_sim1.ok
new file mode 100644
index 00000000..01adfab6
--- /dev/null
+++ b/test/ccs_sim1.ok
@@ -0,0 +1,17 @@
+Startpoint: in1 (input port)
+Endpoint: out1 (output port)
+Path Group: unconstrained
+Path Type: max
+
+ Slew Delay Time Description
+-------------------------------------------------------------------
+ 0.000 0.000 v input external delay
+ 20.000 0.000 0.000 v in1 (in)
+ 20.000 0.000 0.000 v u1/A (INVx8_ASAP7_75t_R)
+ 49.139 20.018 20.018 ^ u1/Y (INVx8_ASAP7_75t_R)
+ 139.668 44.168 64.186 ^ out1 (out)
+ 64.186 data arrival time
+-------------------------------------------------------------------
+(Path is unconstrained)
+
+
diff --git a/test/ccs_sim1.spef b/test/ccs_sim1.spef
new file mode 100644
index 00000000..f4b4594f
--- /dev/null
+++ b/test/ccs_sim1.spef
@@ -0,0 +1,37 @@
+*SPEF "IEEE 1481-1998"
+*DESIGN "top"
+*DATE "Tue Sep 25 11:51:50 2012"
+*VENDOR "handjob"
+*PROGRAM "handjob"
+*VERSION "0.0"
+*DESIGN_FLOW "NETLIST_TYPE_VERILOG"
+*DIVIDER /
+*DELIMITER :
+*BUS_DELIMITER [ ]
+*T_UNIT 1 PS
+*C_UNIT 1 FF
+*R_UNIT 1 KOHM
+*L_UNIT 1 UH
+
+*POWER_NETS VDD
+*GROUND_NETS VSS
+
+*PORTS
+in1 I
+out1 O
+
+*D_NET out1 16.6
+*CONN
+*I u1:Y O
+*I u2:Y O
+*I out1 O
+*CAP
+1 u1:Y 10
+2 u2:Y 10
+3 out1:1 10
+4 out1 50
+*RES
+1 u1:Y out1:1 0.1
+2 u2:Y out1:1 0.1
+3 out1:1 out1 1.0
+*END
diff --git a/test/ccs_sim1.tcl b/test/ccs_sim1.tcl
new file mode 100644
index 00000000..b3b14ea4
--- /dev/null
+++ b/test/ccs_sim1.tcl
@@ -0,0 +1,8 @@
+# ccs_sim parallel inverters
+read_liberty asap7_invbuf.lib.gz
+read_verilog ccs_sim1.v
+link_design top
+read_spef ccs_sim1.spef
+set_input_transition 20 in1
+sta::set_delay_calculator ccs_sim
+report_checks -fields {input_pins slew} -unconstrained -digits 3
diff --git a/test/ccs_sim1.v b/test/ccs_sim1.v
new file mode 100644
index 00000000..9fc0ae6b
--- /dev/null
+++ b/test/ccs_sim1.v
@@ -0,0 +1,7 @@
+module top (in1, out1);
+ input in1;
+ output out1;
+
+ INVx8_ASAP7_75t_R u1 (.A(in1), .Y(out1));
+ INVx8_ASAP7_75t_R u2 (.A(in1), .Y(out1));
+endmodule // top
diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl
index 444cab71..a109a348 100644
--- a/test/regression_vars.tcl
+++ b/test/regression_vars.tcl
@@ -122,6 +122,7 @@ record_example_tests {
}
record_sta_tests {
+ ccs_sim1
verilog_attribute
}