Dmp delay calc inline algs

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2026-05-05 17:08:16 -07:00
parent 7fca318611
commit 801d621d6b
3 changed files with 292 additions and 302 deletions

View File

@ -68,6 +68,17 @@ static const char *dmp_func_index_strings[] = {"y20", "y50", "Ipi"};
static double
exp2(double x);
static double
gateModelRd(const LibertyCell *cell,
const GateTableModel *gate_model,
const RiseFall *rf,
double in_slew,
double c2,
double c1,
const Pvt *pvt);
////////////////////////////////////////////////////////////////
class DmpError : public Exception
{
public:
@ -78,156 +89,13 @@ private:
std::string what_;
};
static double
gateModelRd(const LibertyCell *cell,
const GateTableModel *gate_model,
const RiseFall *rf,
double in_slew,
double c2,
double c1,
const Pvt *pvt);
////////////////////////////////////////////////////////////////
// Base class for Dartu/Menezes/Pileggi algorithm.
// Derived classes handle different cases of zero values in the Pi model.
class DmpAlg : public StaState
DmpError::DmpError(std::string_view what) :
what_(what)
{
public:
DmpAlg(int nr_order,
StaState *sta);
~DmpAlg() override = default;
virtual std::string_view name() = 0;
// Set driver model and pi model parameters for delay calculation.
virtual void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1);
virtual std::pair<double, double> gateDelaySlew() = 0;
virtual std::pair<double, double> loadDelaySlew(const Pin *load_pin,
double elmore);
double ceff() { return ceff_; }
//sta::print(stdout, "DmpError {}\n", what);
}
// Given x_ as a vector of input parameters, fill fvec_ with the
// 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 (vo, dvo_dt).
std::pair<double, double> Vo(double t);
// Load response to driver waveform (vl, dvl/dt).
std::pair<double, double> Vl(double t);
protected:
void luDecomp();
void luSolve();
void newtonRaphson();
// Find driver parameters t0, delta_t, Ceff.
void findDriverParams(double ceff);
std::pair<double, double> gateCapDelaySlew(double ceff);
std::tuple<double, double, double> gateDelays(double ceff);
// Partial derivatives of y(t) jacobian (dydt0, dyddt, dydcl).
std::tuple<double, double, double> dy(double t,
double t0,
double dt,
double cl);
double y0dt(double t,
double cl);
double y0dcl(double t,
double cl);
void showX();
void showFvec();
void showJacobian();
std::pair<double, double> findDriverDelaySlew();
double findVoCrossing(double vth,
double t_lower,
double t_upper);
void showVo();
double findVlCrossing(double vth,
double t_lower,
double t_upper);
void showVl();
void fail(std::string_view reason);
// Output response to vs(t) ramp driving capacitive load (y, t1).
std::pair<double, double> y(double t,
double t0,
double dt,
double cl);
// Output response to unit ramp driving capacitive load.
double y0(double t,
double cl);
// Output response to unit ramp driving pi model load.
// Unit ramp output at pi load (vo, dvo_dt).
virtual std::pair<double, double> V0(double t) = 0;
// Upper bound on time that vo crosses vh.
virtual double voCrossingUpperBound() = 0;
// Load responce to driver unit ramp.
// Unit ramp load response (vl, dvl_dt).
virtual std::pair<double, double> Vl0(double t) = 0;
// Upper bound on time that vl crosses vh.
double vlCrossingUpperBound();
// Inputs to the delay calculator.
const LibertyCell *drvr_cell_;
const LibertyLibrary *drvr_library_;
const Pvt *pvt_;
const GateTableModel *gate_model_;
double in_slew_;
double c2_{0.0};
double rpi_{0.0};
double c1_{0.0};
double rd_;
// Logic threshold (percentage of supply voltage).
double vth_;
// Slew lower limit (percentage of supply voltage).
double vl_;
// Slew upper limit (percentage of supply voltage).
double vh_;
// Table slews are scaled by slew_derate to get
// measured slews from vl to vh.
double slew_derate_;
// Driver parameters calculated by this algorithm.
double t0_;
double dt_;
double ceff_;
// Driver parameter Newton-Raphson state.
int nr_order_;
static constexpr int max_nr_order_ = 3;
std::array<double, max_nr_order_> x_;
std::array<double, max_nr_order_> fvec_;
std::array<std::array<double, max_nr_order_>, max_nr_order_> fjac_;
std::array<double, max_nr_order_> scale_;
std::array<double, max_nr_order_> p_;
std::array<int, max_nr_order_> index_;
// Driver slew used to check load delay.
double drvr_slew_;
double vo_delay_;
// True if the driver parameters are valid for finding the load delays.
bool driver_valid_;
// Load rspf elmore delay.
double elmore_;
double p3_;
// Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0).
static constexpr double driver_param_tol_ = .01;
// Waveform threshold crossing time tolerance (1.0 = 100%).
static constexpr double vth_time_tol_ = .01;
// Max iterations for findRoot.
static constexpr int find_root_max_iter_ = 20;
static inline int newton_raphson_max_iter_ = 100;
// A small number used by luDecomp.
static constexpr double tiny_double_ = 1.0e-20;
};
////////////////////////////////////////////////////////////////
DmpAlg::DmpAlg(int nr_order,
StaState *sta) :
@ -593,33 +461,6 @@ DmpAlg::fail(std::string_view reason)
////////////////////////////////////////////////////////////////
// Capacitive load.
class DmpCap : public DmpAlg
{
public:
DmpCap(StaState *sta);
std::string_view name() override { return "cap"; }
void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
std::pair<double, double> loadDelaySlew(const Pin *,
double elmore) override;
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
};
DmpCap::DmpCap(StaState *sta) :
DmpAlg(1,
sta)
@ -692,53 +533,6 @@ DmpCap::Vl0(double)
////////////////////////////////////////////////////////////////
// No non-zero pi model parameters, two poles, one zero
class DmpPi : public DmpAlg
{
public:
DmpPi(StaState *sta);
std::string_view name() override { return "Pi"; }
void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
private:
void findDriverParamsPi();
double ipiIceff(double t0,
double dt,
double ceff_time,
double ceff);
// Poles/zero.
double p1_{0.0};
double p2_{0.0};
double z1_{0.0};
// Residues.
double k0_{0.0};
double k1_{0.0};
double k2_{0.0};
double k3_{0.0};
double k4_{0.0};
// Ipi coefficients.
double A_{0.0};
double B_{0.0};
double D_{0.0};
};
DmpPi::DmpPi(StaState *sta) :
DmpAlg(3,
sta)
@ -940,18 +734,6 @@ DmpPi::voCrossingUpperBound()
////////////////////////////////////////////////////////////////
// Capacitive load, so Ceff is known.
// Solve for t0, delta t.
class DmpOnePole : public DmpAlg
{
public:
DmpOnePole(StaState *sta);
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
};
DmpOnePole::DmpOnePole(StaState *sta) :
DmpAlg(2,
sta)
@ -1000,40 +782,6 @@ DmpOnePole::voCrossingUpperBound()
////////////////////////////////////////////////////////////////
// C2 = 0, one pole, one zero.
class DmpZeroC2 : public DmpOnePole
{
public:
DmpZeroC2(StaState *sta);
std::string_view name() override { return "c2=0"; }
void init(const LibertyLibrary *drvr_library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
protected:
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
double voCrossingUpperBound() override;
private:
// Pole/zero.
double p1_{0.0};
double z1_{0.0};
// Residues.
double k0_{0.0};
double k1_{0.0};
double k2_{0.0};
double k3_{0.0};
};
DmpZeroC2::DmpZeroC2(StaState *sta) :
DmpOnePole(sta)
{
@ -1260,19 +1008,12 @@ bool DmpCeffDelayCalc::unsuppored_model_warned_ = false;
DmpCeffDelayCalc::DmpCeffDelayCalc(StaState *sta) :
LumpedCapDelayCalc(sta),
dmp_cap_(new DmpCap(sta)),
dmp_pi_(new DmpPi(sta)),
dmp_zero_c2_(new DmpZeroC2(sta))
dmp_cap_(sta),
dmp_pi_(sta),
dmp_zero_c2_(sta)
{
}
DmpCeffDelayCalc::~DmpCeffDelayCalc()
{
delete dmp_cap_;
delete dmp_pi_;
delete dmp_zero_c2_;
}
ArcDcalcResult
DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
const TimingArc *arc,
@ -1362,15 +1103,15 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library,
|| rpi < rd * 1e-3
// c1/Rpi can be ignored.
|| (c1 == 0.0 || c1 < c2 * 1e-3 || rpi == 0.0))
dmp_alg_ = dmp_cap_;
dmp_alg_ = &dmp_cap_;
else if (c2 < c1 * 1e-3)
dmp_alg_ = dmp_zero_c2_;
dmp_alg_ = &dmp_zero_c2_;
else
// The full monty.
dmp_alg_ = dmp_pi_;
dmp_alg_ = &dmp_pi_;
}
else
dmp_alg_ = dmp_cap_;
dmp_alg_ = &dmp_cap_;
dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew,
c2, rpi, c1);
debugPrint(debug_, "dmp_ceff", 3,
@ -1473,15 +1214,9 @@ void
DmpCeffDelayCalc::copyState(const StaState *sta)
{
StaState::copyState(sta);
dmp_cap_->copyState(sta);
dmp_pi_->copyState(sta);
dmp_zero_c2_->copyState(sta);
}
DmpError::DmpError(std::string_view what) :
what_(what)
{
//sta::print(stdout, "DmpError {}\n", what);
dmp_cap_.copyState(sta);
dmp_pi_.copyState(sta);
dmp_zero_c2_.copyState(sta);
}
// This saves about 2.5% in overall run time on designs with SPEF.

View File

@ -32,19 +32,275 @@
namespace sta {
class DmpAlg;
class DmpCap;
class DmpPi;
class DmpZeroC2;
class GateTableModel;
// Base class for Dartu/Menezes/Pileggi algorithm.
// Derived classes handle different cases of zero values in the Pi model.
class DmpAlg : public StaState
{
public:
DmpAlg(int nr_order,
StaState *sta);
~DmpAlg() override = default;
virtual std::string_view name() = 0;
// Set driver model and pi model parameters for delay calculation.
virtual void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1);
virtual std::pair<double, double> gateDelaySlew() = 0;
virtual std::pair<double, double> loadDelaySlew(const Pin *load_pin,
double elmore);
double ceff() { return ceff_; }
// Given x_ as a vector of input parameters, fill fvec_ with the
// 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 (vo, dvo_dt).
std::pair<double, double> Vo(double t);
// Load response to driver waveform (vl, dvl/dt).
std::pair<double, double> Vl(double t);
protected:
void luDecomp();
void luSolve();
void newtonRaphson();
// Find driver parameters t0, delta_t, Ceff.
void findDriverParams(double ceff);
std::pair<double, double> gateCapDelaySlew(double ceff);
std::tuple<double, double, double> gateDelays(double ceff);
// Partial derivatives of y(t) jacobian (dydt0, dyddt, dydcl).
std::tuple<double, double, double> dy(double t,
double t0,
double dt,
double cl);
double y0dt(double t,
double cl);
double y0dcl(double t,
double cl);
void showX();
void showFvec();
void showJacobian();
std::pair<double, double> findDriverDelaySlew();
double findVoCrossing(double vth,
double t_lower,
double t_upper);
void showVo();
double findVlCrossing(double vth,
double t_lower,
double t_upper);
void showVl();
void fail(std::string_view reason);
// Output response to vs(t) ramp driving capacitive load (y, t1).
std::pair<double, double> y(double t,
double t0,
double dt,
double cl);
// Output response to unit ramp driving capacitive load.
double y0(double t,
double cl);
// Output response to unit ramp driving pi model load.
// Unit ramp output at pi load (vo, dvo_dt).
virtual std::pair<double, double> V0(double t) = 0;
// Upper bound on time that vo crosses vh.
virtual double voCrossingUpperBound() = 0;
// Load responce to driver unit ramp.
// Unit ramp load response (vl, dvl_dt).
virtual std::pair<double, double> Vl0(double t) = 0;
// Upper bound on time that vl crosses vh.
double vlCrossingUpperBound();
// Inputs to the delay calculator.
const LibertyCell *drvr_cell_;
const LibertyLibrary *drvr_library_;
const Pvt *pvt_;
const GateTableModel *gate_model_;
double in_slew_;
double c2_{0.0};
double rpi_{0.0};
double c1_{0.0};
double rd_;
// Logic threshold (percentage of supply voltage).
double vth_;
// Slew lower limit (percentage of supply voltage).
double vl_;
// Slew upper limit (percentage of supply voltage).
double vh_;
// Table slews are scaled by slew_derate to get
// measured slews from vl to vh.
double slew_derate_;
// Driver parameters calculated by this algorithm.
double t0_;
double dt_;
double ceff_;
// Driver parameter Newton-Raphson state.
int nr_order_;
static constexpr int max_nr_order_ = 3;
std::array<double, max_nr_order_> x_;
std::array<double, max_nr_order_> fvec_;
std::array<std::array<double, max_nr_order_>, max_nr_order_> fjac_;
std::array<double, max_nr_order_> scale_;
std::array<double, max_nr_order_> p_;
std::array<int, max_nr_order_> index_;
// Driver slew used to check load delay.
double drvr_slew_;
double vo_delay_;
// True if the driver parameters are valid for finding the load delays.
bool driver_valid_;
// Load rspf elmore delay.
double elmore_;
double p3_;
// Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0).
static constexpr double driver_param_tol_ = .01;
// Waveform threshold crossing time tolerance (1.0 = 100%).
static constexpr double vth_time_tol_ = .01;
// Max iterations for findRoot.
static constexpr int find_root_max_iter_ = 20;
static inline int newton_raphson_max_iter_ = 100;
// A small number used by luDecomp.
static constexpr double tiny_double_ = 1.0e-20;
};
// Capacitive load.
class DmpCap : public DmpAlg
{
public:
DmpCap(StaState *sta);
std::string_view name() override { return "cap"; }
void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
std::pair<double, double> loadDelaySlew(const Pin *,
double elmore) override;
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
};
// No non-zero pi model parameters, two poles, one zero
class DmpPi : public DmpAlg
{
public:
DmpPi(StaState *sta);
std::string_view name() override { return "Pi"; }
void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
private:
void findDriverParamsPi();
double ipiIceff(double t0,
double dt,
double ceff_time,
double ceff);
// Poles/zero.
double p1_{0.0};
double p2_{0.0};
double z1_{0.0};
// Residues.
double k0_{0.0};
double k1_{0.0};
double k2_{0.0};
double k3_{0.0};
double k4_{0.0};
// Ipi coefficients.
double A_{0.0};
double B_{0.0};
double D_{0.0};
};
// Capacitive load, so Ceff is known.
// Solve for t0, delta t.
class DmpOnePole : public DmpAlg
{
public:
DmpOnePole(StaState *sta);
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
};
// C2 = 0, one pole, one zero.
class DmpZeroC2 : public DmpOnePole
{
public:
DmpZeroC2(StaState *sta);
std::string_view name() override { return "c2=0"; }
void init(const LibertyLibrary *drvr_library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
protected:
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
double voCrossingUpperBound() override;
private:
// Pole/zero.
double p1_{0.0};
double z1_{0.0};
// Residues.
double k0_{0.0};
double k1_{0.0};
double k2_{0.0};
double k3_{0.0};
};
// Delay calculator using Dartu/Menezes/Pileggi effective capacitance
// algorithm for RSPF loads.
class DmpCeffDelayCalc : public LumpedCapDelayCalc
{
public:
DmpCeffDelayCalc(StaState *sta);
~DmpCeffDelayCalc() override;
bool reduceSupported() const override { return true; }
ArcDcalcResult gateDelay(const Pin *drvr_pin,
const TimingArc *arc,
@ -94,10 +350,9 @@ protected:
private:
// Dmp algorithms for each special pi model case.
// These objects are reused to minimize make/deletes.
DmpCap *dmp_cap_;
DmpPi *dmp_pi_;
DmpZeroC2 *dmp_zero_c2_;
DmpCap dmp_cap_;
DmpPi dmp_pi_;
DmpZeroC2 dmp_zero_c2_;
DmpAlg *dmp_alg_{nullptr};
};

View File

@ -77,7 +77,7 @@ DmpCeffElmoreDelayCalc::DmpCeffElmoreDelayCalc(StaState *sta) :
ArcDelayCalc *
DmpCeffElmoreDelayCalc::copy()
{
return new DmpCeffElmoreDelayCalc(this);
return new DmpCeffElmoreDelayCalc(*this);
}
ArcDcalcResult
@ -213,7 +213,7 @@ DmpCeffTwoPoleDelayCalc::DmpCeffTwoPoleDelayCalc(StaState *sta) :
ArcDelayCalc *
DmpCeffTwoPoleDelayCalc::copy()
{
return new DmpCeffTwoPoleDelayCalc(this);
return new DmpCeffTwoPoleDelayCalc(*this);
}
Parasitic *