dmp tweaks

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2024-01-21 10:19:52 -07:00
parent 3ae813a207
commit 6a4e610e07
4 changed files with 381 additions and 263 deletions

View File

@ -70,6 +70,7 @@ set(STA_SOURCE
dcalc/DelayCalcBase.cc
dcalc/DmpCeff.cc
dcalc/DmpDelayCalc.cc
dcalc/FindRoot.cc
dcalc/GraphDelayCalc.cc
dcalc/LumpedCapDelayCalc.cc
dcalc/NetCaps.cc

View File

@ -38,6 +38,7 @@
#include "Parasitics.hh"
#include "DcalcAnalysisPt.hh"
#include "ArcDelayCalc.hh"
#include "FindRoot.hh"
namespace sta {
@ -92,24 +93,6 @@ gateModelRd(const LibertyCell *cell,
const Pvt *pvt,
bool pocv_enabled);
static void
evalVoEqns(void *state,
double x,
double &y,
double &dy);
static void
evalVlEqns(void *state,
double x,
double &y,
double &dy);
static double
findRoot(void (*func)(void *state, double x, double &y, double &dy),
void *state,
double x1,
double x2,
double x_tol,
int max_iter);
static void
newtonRaphson(const int max_iter,
double x[],
const int n,
@ -154,10 +137,12 @@ public:
double c2,
double rpi,
double c1);
virtual void gateDelaySlew(double &delay,
virtual void gateDelaySlew(// Return values.
double &delay,
double &slew) = 0;
virtual void loadDelaySlew(const Pin *load_pin,
double elmore,
// Return values.
ArcDelay &delay,
Slew &slew);
double ceff() { return ceff_; }
@ -166,29 +151,34 @@ public:
// equations evaluated at x_ and fjac_ with the jabobian evaluated at x_.
virtual void evalDmpEqns() = 0;
// Output response to vs(t) ramp driving pi model load.
double vo(double t);
double dVoDt(double t);
void Vo(double t,
// Return values.
double &vo,
double &dol_dt);
// Load responce to driver waveform.
double vl(double t);
double dVlDt(double t);
double vCross() { return v_cross_; }
void Vl(double t,
// Return values.
double &vl,
double &dvl_dt);
protected:
// Find driver parameters t0, delta_t, Ceff.
void findDriverParams(double ceff);
void gateCapDelaySlew(double cl,
// Return values.
double &delay,
double &slew);
void gateDelays(double ceff,
// Return values.
double &t_vth,
double &t_vl,
double &slew);
virtual double dv0dt(double t) = 0;
// Partial derivatives of y(t) (jacobian).
void dy(double t,
double t0,
double dt,
double cl,
// Return values.
double &dydt0,
double &dyddt,
double &dydcl);
@ -199,11 +189,16 @@ protected:
void showX();
void showFvec();
void showJacobian();
void findDriverDelaySlew(double &delay,
void findDriverDelaySlew(// Return values.
double &delay,
double &slew);
double findVoCrossing(double vth);
double findVoCrossing(double vth,
double lower_bound,
double upper_bound);
void showVo();
double findVlCrossing(double vth);
double findVlCrossing(double vth,
double lower_bound,
double upper_bound);
void showVl();
void fail(const char *reason);
@ -216,11 +211,17 @@ protected:
double y0(double t,
double cl);
// Output response to unit ramp driving pi model load.
virtual double v0(double t) = 0;
virtual void V0(double t,
// Return values.
double &vo,
double &dvo_dt) = 0;
// Upper bound on time that vo crosses vh.
virtual double voCrossingUpperBound() = 0;
// Load responce to driver unit ramp.
virtual double vl0(double t) = 0;
virtual void Vl0(double t,
// Return values.
double &vl,
double &dvl_dt) = 0;
// Upper bound on time that vl crosses vh.
double vlCrossingUpperBound();
@ -267,12 +268,6 @@ protected:
// Load rspf elmore delay.
double elmore_;
double p3_;
private:
virtual double dvl0dt(double t) = 0;
// Implicit argument passed to evalVoEqns, evalVlEqns.
double v_cross_;
};
DmpAlg::DmpAlg(int nr_order,
@ -362,7 +357,8 @@ DmpAlg::findDriverParams(double ceff)
void
DmpAlg::gateCapDelaySlew(double ceff,
double &delay,
// Return values.
double &delay,
double &slew)
{
ArcDelay model_delay;
@ -375,7 +371,8 @@ DmpAlg::gateCapDelaySlew(double ceff,
void
DmpAlg::gateDelays(double ceff,
double &t_vth,
// Return values.
double &t_vth,
double &t_vl,
double &slew)
{
@ -479,58 +476,68 @@ DmpAlg::showJacobian()
}
void
DmpAlg::findDriverDelaySlew(double &delay,
DmpAlg::findDriverDelaySlew(// Return values.
double &delay,
double &slew)
{
delay = findVoCrossing(vth_);
double tl = findVoCrossing(vl_);
double th = findVoCrossing(vh_);
double t_upper = voCrossingUpperBound();
delay = findVoCrossing(vth_, t0_, t_upper);
double tl = findVoCrossing(vl_, t0_, delay);
double th = findVoCrossing(vh_, delay, t_upper);
// Convert measured slew to table slew.
slew = (th - tl) / slew_derate_;
}
// Find t such that vo(t)=v.
double
DmpAlg::findVoCrossing(double vth)
DmpAlg::findVoCrossing(double vth,
double t_lower,
double t_upper)
{
v_cross_ = vth;
double ub = voCrossingUpperBound();
return findRoot(evalVoEqns, this, t0_, ub, vth_time_tol, find_root_max_iter);
FindRootFunc vo_func = [=] (double t,
double &y,
double &dy) {
double vo, vo_dt;
Vo(t, vo, vo_dt);
y = vo - vth;
dy = vo_dt;
};
bool fail;
double t_vth = findRoot(vo_func, t_lower, t_upper, vth_time_tol,
find_root_max_iter, fail);
if (fail)
throw DmpError("findRoot failed");
return t_vth;
}
static void
evalVoEqns(void *state,
double x,
double &y,
double &dy)
{
DmpAlg *pi_ceff = reinterpret_cast<DmpAlg *>(state);
y = pi_ceff->vo(x) - pi_ceff->vCross();
dy = pi_ceff->dVoDt(x);
}
double
DmpAlg::vo(double t)
void
DmpAlg::Vo(double t,
// Return values.
double &vo,
double &dvo_dt)
{
double t1 = t - t0_;
if (t1 <= 0.0)
return 0.0;
else if (t1 <= dt_)
return v0(t1) / dt_;
else
return (v0(t1) - v0(t1 - dt_)) / dt_;
}
if (t1 <= 0.0) {
vo = 0.0;
dvo_dt = 0.0;
}
else if (t1 <= dt_) {
double v0, dv0_dt;
V0(t1, v0, dv0_dt);
double
DmpAlg::dVoDt(double t)
{
double t1 = t - t0_;
if (t1 <= 0)
return 0.0;
else if (t1 <= dt_)
return dv0dt(t1) / dt_;
else
return (dv0dt(t1) - dv0dt(t1 - dt_)) / dt_;
vo = v0 / dt_;
dvo_dt = dv0_dt / dt_;
}
else {
double v0, dv0_dt;
V0(t1, v0, dv0_dt);
double v0_dt, dv0_dt_dt;
V0(t1 - dt_, v0_dt, dv0_dt_dt);
vo = (v0 - v0_dt) / dt_;
dvo_dt = (dv0_dt - dv0_dt_dt) / dt_;
}
}
void
@ -538,8 +545,11 @@ DmpAlg::showVo()
{
report_->reportLine(" t vo(t)");
double ub = voCrossingUpperBound();
for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0)
report_->reportLine(" %g %g", t, vo(t));
for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) {
double vo, dvo_dt;
Vo(t, vo, dvo_dt);
report_->reportLine(" %g %g", t, vo);
}
}
void
@ -563,9 +573,11 @@ DmpAlg::loadDelaySlew(const Pin *,
showVl();
elmore_ = elmore;
p3_ = 1.0 / elmore;
double load_delay = findVlCrossing(vth_);
double tl = findVlCrossing(vl_);
double th = findVlCrossing(vh_);
double t_lower = t0_;
double t_upper = vlCrossingUpperBound();
double load_delay = findVlCrossing(vth_, t_lower, t_upper);
double tl = findVlCrossing(vl_, t_lower, load_delay);
double th = findVlCrossing(vh_, load_delay, t_upper);
// Measure delay from Vo, the load dependent source excitation.
double delay1 = load_delay - vo_delay_;
// Convert measured slew to reported/table slew.
@ -595,13 +607,25 @@ DmpAlg::loadDelaySlew(const Pin *,
}
// Find t such that vl(t)=v.
// Return true if successful.
double
DmpAlg::findVlCrossing(double vth)
DmpAlg::findVlCrossing(double vth,
double t_lower,
double t_upper)
{
v_cross_ = vth;
double ub = vlCrossingUpperBound();
return findRoot(evalVlEqns, this, t0_, ub, vth_time_tol, find_root_max_iter);
FindRootFunc vl_func = [=] (double t,
double &y,
double &dy) {
double vl, vl_dt;
Vl(t, vl, vl_dt);
y = vl - vth;
dy = vl_dt;
};
bool fail;
double t_vth = findRoot(vl_func, t_lower, t_upper, vth_time_tol,
find_root_max_iter, fail);
if (fail)
throw DmpError("findRoot failed");
return t_vth;
}
double
@ -610,39 +634,33 @@ DmpAlg::vlCrossingUpperBound()
return voCrossingUpperBound() + elmore_ * 2.0;
}
static void
evalVlEqns(void *state,
double x,
double &y,
double &dy)
{
DmpAlg *pi_ceff = reinterpret_cast<DmpAlg *>(state);
y = pi_ceff->vl(x) - pi_ceff->vCross();
dy = pi_ceff->dVlDt(x);
}
double
DmpAlg::vl(double t)
void
DmpAlg::Vl(double t,
// Return values.
double &vl,
double &dvl_dt)
{
double t1 = t - t0_;
if (t1 <= 0)
return 0.0;
else if (t1 <= dt_)
return vl0(t1) / dt_;
else
return (vl0(t1) - vl0(t1 - dt_)) / dt_;
}
if (t1 <= 0.0) {
vl = 0.0;
dvl_dt = 0.0;
}
else if (t1 <= dt_) {
double vl0, dvl0_dt;
Vl0(t1, vl0, dvl0_dt);
vl = vl0 / dt_;
dvl_dt = dvl0_dt / dt_;
}
else {
double vl0, dvl0_dt;
Vl0(t1, vl0, dvl0_dt);
double
DmpAlg::dVlDt(double t)
{
double t1 = t - t0_;
if (t1 <= 0)
return 0.0;
else if (t1 <= dt_)
return dvl0dt(t1) / dt_;
else
return (dvl0dt(t1) - dvl0dt(t1 - dt_)) / dt_;
double vl0_dt, dvl0_dt_dt;
Vl0(t1 - dt_, vl0_dt, dvl0_dt_dt);
vl = (vl0 - vl0_dt) / dt_;
dvl_dt = (dvl0_dt - dvl0_dt_dt) / dt_;
}
}
void
@ -650,8 +668,11 @@ DmpAlg::showVl()
{
report_->reportLine(" t vl(t)");
double ub = vlCrossingUpperBound();
for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0)
report_->reportLine(" %g %g", t, vl(t));
for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) {
double vl, dvl_dt;
Vl(t, vl, dvl_dt);
report_->reportLine(" %g %g", t, vl);
}
}
void
@ -685,20 +706,26 @@ public:
double c2,
double rpi,
double c1);
virtual void gateDelaySlew(double &delay,
virtual void gateDelaySlew(// Return values.
double &delay,
double &slew);
virtual void loadDelaySlew(const Pin *,
double elmore,
ArcDelay &delay,
// Return values.
ArcDelay &delay,
Slew &slew);
virtual void evalDmpEqns();
virtual double voCrossingUpperBound();
private:
virtual double v0(double t);
virtual double dv0dt(double t);
virtual double vl0(double t);
virtual double dvl0dt(double t);
virtual void V0(double t,
// Return values.
double &vo,
double &dvo_dt);
virtual void Vl0(double t,
// Return values.
double &vl,
double &dvl_dt);
};
DmpCap::DmpCap(StaState *sta):
@ -725,7 +752,8 @@ DmpCap::init(const LibertyLibrary *drvr_library,
}
void
DmpCap::gateDelaySlew(double &delay,
DmpCap::gateDelaySlew(// Return values.
double &delay,
double &slew)
{
debugPrint(debug_, "dmp_ceff", 3, " ceff = %s",
@ -749,16 +777,14 @@ DmpCap::evalDmpEqns()
{
}
double
DmpCap::v0(double)
void
DmpCap::V0(double,
// Return values.
double &vo,
double &dvo_dt)
{
return 0.0;
}
double
DmpCap::dv0dt(double)
{
return 0.0;
vo = 0.0;
dvo_dt = 0.0;
}
double
@ -767,16 +793,14 @@ DmpCap::voCrossingUpperBound()
return 0.0;
}
double
DmpCap::vl0(double)
void
DmpCap::Vl0(double ,
// Return values.
double &vl,
double &dvl_dt)
{
return 0.0;
}
double
DmpCap::dvl0dt(double)
{
return 0.0;
vl = 0.0;
dvl_dt = 0.0;
}
////////////////////////////////////////////////////////////////
@ -797,21 +821,26 @@ public:
double c2,
double rpi,
double c1);
virtual void gateDelaySlew(double &delay,
virtual void gateDelaySlew(// Return values.
double &delay,
double &slew);
virtual void evalDmpEqns();
virtual double voCrossingUpperBound();
private:
void findDriverParamsPi();
virtual double v0(double t);
virtual double dv0dt(double t);
double ipiIceff(double t0,
double dt,
double ceff_time,
double ceff);
virtual double vl0(double t);
virtual double dvl0dt(double t);
virtual void V0(double t,
// Return values.
double &vo,
double &dvo_dt);
virtual void Vl0(double t,
// Return values.
double &vl,
double &dvl_dt);
// Poles/zero.
double p1_;
@ -883,7 +912,8 @@ DmpPi::init(const LibertyLibrary *drvr_library,
}
void
DmpPi::gateDelaySlew(double &delay,
DmpPi::gateDelaySlew(// Return values.
double &delay,
double &slew)
{
driver_valid_ = false;
@ -1012,27 +1042,35 @@ DmpPi::ipiIceff(double, double dt,
return ipi - iceff;
}
double
DmpPi::v0(double t)
void
DmpPi::V0(double t,
// Return values.
double &vo,
double &dvo_dt)
{
return k0_ * (k1_ + k2_ * t + k3_ * exp2(-p1_ * t) + k4_ * exp2(-p2_ * t));
double exp_p1 = exp2(-p1_ * t);
double exp_p2 = exp2(-p2_ * t);
vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1 + k4_ * exp_p2);
dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1 - k4_ * p2_ * exp_p2);
}
double
DmpPi::dv0dt(double t)
{
return k0_ * (k2_ - k3_ * p1_ * exp2(-p1_ * t) - k4_ * p2_ * exp2(-p2_ * t));
}
double
DmpPi::vl0(double t)
void
DmpPi::Vl0(double t,
// Return values.
double &vl,
double &dvl_dt)
{
double D1 = k0_ * (k1_ - k2_ / p3_);
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
double D4 = -p3_ * k0_ * k4_ / (p2_ - p3_);
double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_)
+ p3_ * k4_ / (p2_ - p3_));
return D1 + t + D3 * exp2(-p1_ * t) + D4 * exp2(-p2_ * t) + D5 * exp2(-p3_ * t);
double exp_p1 = exp2(-p1_ * t);
double exp_p2 = exp2(-p2_ * t);
double exp_p3 = exp2(-p3_ * t);
vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3;
dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2
- D5 * p3_ * exp_p3;
}
double
@ -1041,17 +1079,6 @@ DmpPi::voCrossingUpperBound()
return t0_ + dt_ + (c1_ + c2_) * (rd_ + rpi_) * 2.0;
}
double
DmpPi::dvl0dt(double t)
{
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
double D4 = -p3_ * k0_ * k4_ / (p2_ - p3_);
double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_)
+ p3_ * k4_ / (p2_ - p3_));
return 1.0 - D3 * p1_ * exp2(-p1_ * t) - D4 * p2_ * exp2(-p2_ * t)
- D5 * p3_ * exp2(-p3_ * t);
}
////////////////////////////////////////////////////////////////
// Capacitive load, so Ceff is known.
@ -1129,14 +1156,19 @@ public:
double c2,
double rpi,
double c1);
virtual void gateDelaySlew(double &delay,
virtual void gateDelaySlew(// Return values.
double &delay,
double &slew);
private:
virtual double v0(double t);
virtual double dv0dt(double t);
virtual double vl0(double t);
virtual double dvl0dt(double t);
virtual void V0(double t,
// Return values.
double &vo,
double &dvo_dt);
virtual void Vl0(double t,
// Return values.
double &vl,
double &dvl_dt);
virtual double voCrossingUpperBound();
// Pole/zero.
@ -1187,7 +1219,8 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library,
}
void
DmpZeroC2::gateDelaySlew(double &delay,
DmpZeroC2::gateDelaySlew(// Return values.
double &delay,
double &slew)
{
try {
@ -1207,33 +1240,30 @@ DmpZeroC2::gateDelaySlew(double &delay,
drvr_slew_ = slew;
}
double
DmpZeroC2::v0(double t)
void
DmpZeroC2::V0(double t,
// Return values.
double &vo,
double &dvo_dt)
{
return k0_ * (k1_ + k2_ * t + k3_ * exp2(-p1_ * t));
double exp_p1 = exp2(-p1_ * t);
vo = k0_ * (k1_ + k2_ * t + k3_ * exp_p1);
dvo_dt = k0_ * (k2_ - k3_ * p1_ * exp_p1);
}
double
DmpZeroC2::dv0dt(double t)
{
return k0_ * (k2_ - k3_ * p1_ * exp2(-p1_ * t));
}
double
DmpZeroC2::vl0(double t)
void
DmpZeroC2::Vl0(double t,
// Return values.
double &vl,
double &dvl_dt)
{
double D1 = k0_ * (k1_ - k2_ / p3_);
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_));
return D1 + t + D3 * exp2(-p1_ * t) + D5 * exp2(-p3_ * t);
}
double
DmpZeroC2::dvl0dt(double t)
{
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_));
return 1.0 - D3 * p1_ * exp2(-p1_ * t) - D5 * p3_ * exp2(-p3_ * t);
double exp_p1 = exp2(-p1_ * t);
double exp_p3 = exp2(-p3_ * t);
vl = D1 + t + D3 * exp_p1 + D5 * exp_p3;
dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D5 * p3_ * exp_p3;
}
double
@ -1244,70 +1274,6 @@ DmpZeroC2::voCrossingUpperBound()
////////////////////////////////////////////////////////////////
// Find the root of a function between x1 and x2 using a combination
// of Newton-Raphson and bisection search.
// x_tol is a percentage that change in x must be less than (1.0 = 100%).
// error is non-null if a problem occurs.
static double
findRoot(void (*func)(void *state, double x, double &y, double &dy),
void *state,
double x1,
double x2,
double x_tol,
int max_iter)
{
double y1, y2, dy;
func(state, x1, y1, dy);
func(state, x2, y2, dy);
if ((y1 > 0.0 && y2 > 0.0) || (y1 < 0.0 && y2 < 0.0))
throw DmpError("findRoot: initial bounds do not surround a root");
if (y1 == 0.0)
return x1;
if (y2 == 0.0)
return x2;
if (y1 > 0.0) {
// Swap x1/x2 so func(x1) < 0.
double tmp = x1;
x1 = x2;
x2 = tmp;
}
double root = (x1 + x2) * 0.5;
double dx_prev = abs(x2 - x1);
double dx = dx_prev;
double y;
func(state, root, y, dy);
for (int iter = 0; iter < max_iter; iter++) {
// Newton/raphson out of range.
if ((((root - x2) * dy - y) * ((root - x1) * dy - y) > 0.0)
// Not decreasing fast enough.
|| (abs(2.0 * y) > abs(dx_prev * dy))) {
// Bisect x1/x2 interval.
dx_prev = dx;
dx = (x2 - x1) * 0.5;
root = x1 + dx;
}
else {
dx_prev = dx;
dx = y / dy;
root -= dx;
}
if (abs(dx) <= x_tol * abs(root))
// Converged.
return root;
func(state, root, y, dy);
if (y < 0.0)
x1 = root;
else
x2 = root;
}
throw DmpError("findRoot: max iterations exceeded");
}
// Newton-Raphson iteration to find zeros of a function.
// x_tol is percentage that all changes in x must be less than (1.0 = 100%).
// Eval(state) is called to fill fvec and fjac (returns false if fails).
@ -1683,7 +1649,8 @@ gateModelRd(const LibertyCell *cell,
}
void
DmpCeffDelayCalc::gateDelaySlew(double &delay,
DmpCeffDelayCalc::gateDelaySlew(// Return values.
double &delay,
double &slew)
{
dmp_alg_->gateDelaySlew(delay, slew);

102
dcalc/FindRoot.cc Normal file
View File

@ -0,0 +1,102 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2023, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "FindRoot.hh"
namespace sta {
double
findRoot(FindRootFunc func,
double x1,
double x2,
double x_tol,
int max_iter,
// Return value.
bool &fail)
{
double y1, y2, dy1;
func(x1, y1, dy1);
func(x2, y2, dy1);
return findRoot(func, x1, y1, x2, y2, x_tol, max_iter, fail);
}
double
findRoot(FindRootFunc func,
double x1,
double y1,
double x2,
double y2,
double x_tol,
int max_iter,
// Return value.
bool &fail)
{
if ((y1 > 0.0 && y2 > 0.0) || (y1 < 0.0 && y2 < 0.0)) {
// Initial bounds do not surround a root.
fail = true;
return 0.0;
}
if (y1 == 0.0) {
fail = false;
return x1;
}
if (y2 == 0.0) {
fail = false;
return x2;
}
if (y1 > 0.0)
// Swap x1/x2 so func(x1) < 0.
std::swap(x1, x2);
double root = (x1 + x2) * 0.5;
double dx_prev = abs(x2 - x1);
double dx = dx_prev;
double y, dy;
func(root, y, dy);
for (int iter = 0; iter < max_iter; iter++) {
// Newton/raphson out of range.
if ((((root - x2) * dy - y) * ((root - x1) * dy - y) > 0.0)
// Not decreasing fast enough.
|| (abs(2.0 * y) > abs(dx_prev * dy))) {
// Bisect x1/x2 interval.
dx_prev = dx;
dx = (x2 - x1) * 0.5;
root = x1 + dx;
}
else {
dx_prev = dx;
dx = y / dy;
root -= dx;
}
if (abs(dx) <= x_tol * abs(root)) {
// Converged.
fail = false;
return root;
}
func(root, y, dy);
if (y < 0.0)
x1 = root;
else
x2 = root;
}
fail = true;
return root;
}
} // namespace

48
dcalc/FindRoot.hh Normal file
View File

@ -0,0 +1,48 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2023, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <algorithm> // abs, min
namespace sta {
typedef const std::function<void (double x,
// Return values.
double &y,
double &dy)> FindRootFunc;
double
findRoot(FindRootFunc func,
double x1,
double x2,
double x_tol,
int max_iter,
// Return value.
bool &fail);
double
findRoot(FindRootFunc func,
double x1,
double y1,
double x2,
double y2,
double x_tol,
int max_iter,
// Return value.
bool &fail);
} // namespace