From bfb31747844fd0cc1bac6fed499e95f75d91592a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 2 Nov 2020 17:14:48 -0800 Subject: [PATCH 1/9] dcalc error handling --- dcalc/DmpCeff.cc | 186 ++++++++++++++++++++++++----------------------- 1 file changed, 97 insertions(+), 89 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index fac600ca..b212319a 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -96,7 +96,7 @@ findRoot(void (*func)(void *state, double x, double &y, double &dy), double x_tol, int max_iter, const char *&error); -static bool +static const char * newtonRaphson(const int max_iter, double x[], const int n, @@ -110,19 +110,17 @@ newtonRaphson(const int max_iter, double **fjac, int *index, double *p, - double *scale, - const char *&error); + double *scale); static void luSolve(double **a, const int size, const int *index, double b[]); -static bool +static const char * luDecomp(double **a, const int size, int *index, - double *scale, - const char *&error); + double *scale); //////////////////////////////////////////////////////////////// @@ -167,7 +165,7 @@ public: protected: // Find driver parameters t0, delta_t, Ceff. - bool findDriverParams(double &ceff); + const char *findDriverParams(double ceff); void gateCapDelaySlew(double cl, double &delay, double &slew); @@ -191,10 +189,10 @@ protected: void showX(); void showFvec(); void showJacobian(); - bool findDriverDelaySlew(double &delay, - double &slew); - bool findVoCrossing(double vth, - double &t); + const char *findDriverDelaySlew(double &delay, + double &slew); + double findVoCrossing(double vth, + const char *&error); void showVo(); bool findVlCrossing(double vth, double &t); @@ -335,21 +333,23 @@ DmpAlg::init(const LibertyLibrary *drvr_library, } // Find Ceff, delta_t and t0 for the driver. -// Caller must initialize/retrieve x_[DmpParam::ceff] because -// order 2 eqns don't have a ceff eqn. -// Return true if successful. -bool -DmpAlg::findDriverParams(double &ceff) +// Return error msg on failure. +const char * +DmpAlg::findDriverParams(double ceff) { + x_[DmpParam::ceff] = ceff; double t_vth, t_vl, slew; gateDelays(ceff, t_vth, t_vl, slew); double dt = slew / (vh_ - vl_); double t0 = t_vth + log(1.0 - vth_) * rd_ * ceff - vth_ * dt; x_[DmpParam::dt] = dt; x_[DmpParam::t0] = t0; - const char *nr_error; - if (newtonRaphson(100, x_, nr_order_, driver_param_tol, evalDmpEqnsState, - this, fvec_, fjac_, index_, p_, scale_, nr_error)) { + const char *error = newtonRaphson(100, x_, nr_order_, driver_param_tol, + evalDmpEqnsState, + this, fvec_, fjac_, index_, p_, scale_); + if (error) + return error; + else { t0_ = x_[DmpParam::t0]; dt_ = x_[DmpParam::dt]; debugPrint3(debug_, "delay_calc", 3, " t0 = %s dt = %s ceff = %s\n", @@ -358,11 +358,7 @@ DmpAlg::findDriverParams(double &ceff) units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); if (debug_->check("delay_calc", 4)) showVo(); - return true; - } - else { - fail(nr_error); - return false; + return nullptr; } } @@ -429,14 +425,18 @@ DmpAlg::dy(double t, double t0, double dt, double cl, + // Return values. double &dydt0, double &dyddt, double &dydcl) { double t1 = t - t0; +#if 0 if (t1 <= 0.0) dydt0 = dyddt = dydcl = 0.0; - else if (t1 <= dt) { + else +#endif +if (t1 <= dt) { dydt0 = -y0dt(t1, cl) / dt; dyddt = -y0(t1, cl) / (dt * dt); dydcl = y0dcl(t1, cl) / dt; @@ -492,36 +492,34 @@ DmpAlg::showJacobian() } } -// Return true if successful. -bool +// Return error msg on failure. +const char * DmpAlg::findDriverDelaySlew(double &delay, double &slew) { - double tl, th; - if (findVoCrossing(vth_, delay) - && findVoCrossing(vl_, tl) - && findVoCrossing(vh_, th)) { - slew = (th - tl) / slew_derate_; - return true; - } - else - return false; + const char *error = nullptr; + delay = findVoCrossing(vth_, error); + if (error) + return error; + double tl = findVoCrossing(vl_, error); + if (error) + return error; + double th = findVoCrossing(vh_, error); + if (error) + return error; + slew = (th - tl) / slew_derate_; + return nullptr; } // Find t such that vo(t)=v. // Return true if successful. -bool +double DmpAlg::findVoCrossing(double vth, - double &t) + const char *&error) { v_cross_ = vth; double ub = voCrossingUpperBound(); - const char *error; - t = findRoot(evalVoEqns, this, t0_, ub, vth_time_tol, find_root_max_iter, - error); - if (error) - fail(error); - return (error == nullptr); + return findRoot(evalVoEqns, this, t0_, ub, vth_time_tol, find_root_max_iter, error); } static void @@ -698,12 +696,12 @@ DmpAlg::fail(const char *reason) { // Allow only failures to be reported with a unique debug flag. if (debug_->check("delay_calc", 1) || debug_->check("delay_calc_dmp", 1)) - debug_->print("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s\n", + debug_->print("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s\n", reason, units_->capacitanceUnit()->asString(c2_), units_->resistanceUnit()->asString(rpi_), - units_->capacitanceUnit()->asString(c1_)); - + units_->capacitanceUnit()->asString(c1_), + UNITS_->resistanceUnit()->asString(rd_)); } //////////////////////////////////////////////////////////////// @@ -846,7 +844,7 @@ public: virtual double voCrossingUpperBound(); private: - bool findDriverParamsPi(); + const char *findDriverParamsPi(); virtual double v0(double t); virtual double dv0dt(double t); double ipiIceff(double t0, @@ -930,7 +928,14 @@ void DmpPi::gateDelaySlew(double &delay, double &slew) { - if (findDriverParamsPi()) { + const char *error = findDriverParamsPi(); + if (error) { + fail(error); + // Driver calculation failed - use Ceff=c1+c2. + ceff_ = c1_ + c2_; + gateCapDelaySlew(ceff_, delay, slew); + } + else { ceff_ = x_[DmpParam::ceff]; driver_valid_ = true; double table_slew; @@ -940,25 +945,25 @@ DmpPi::gateDelaySlew(double &delay, // Vo slew is more accurate than table // (-8% max, -3% avg vs -32% max, -12% avg). // Need Vo delay to measure load wire delay waveform. - if (!findDriverDelaySlew(vo_delay_, slew)) - // Fall back to table slew if findDriverDelaySlew fails. + const char *error = findDriverDelaySlew(vo_delay_, slew); + if (error) { + fail(error); + // Fall back to table slew. slew = table_slew; - } - else { - // Driver calculation failed - use Ceff=c1+c2. - ceff_ = c1_ + c2_; - gateCapDelaySlew(ceff_, delay, slew); + } } // Save for wire delay calc. gate_slew_ = slew; } -bool +const char * DmpPi::findDriverParamsPi() { - double ceff = c1_ + c2_; - x_[DmpParam::ceff] = ceff; - return findDriverParams(x_[DmpParam::ceff]); + const char *error; + error = findDriverParams(c2_ + c1_); + if (error) + error = findDriverParams(c2_); + return error; } // Given x_ as a vector of input parameters, fill fvec_ with the @@ -1222,11 +1227,23 @@ void DmpZeroC2::gateDelaySlew(double &delay, double &slew) { - driver_valid_ = findDriverParams(c1_); +#if 0 + // Not converging to a reasonable solution. set_load10 + const char *error = findDriverParams(c1_); ceff_ = c1_; - if (!findDriverDelaySlew(delay, slew)) - // Fall back to table slew if findDriverDelaySlew fails. + driver_valid_ = (error == nullptr); + if (error == nullptr) + error = findDriverDelaySlew(delay, slew); + if (error) { + fail(error); + // Fall back to table slew. gateCapDelaySlew(ceff_, delay, slew); + } + vo_delay_ = delay; + gate_slew_ = slew; +#endif + ceff_ = c1_; + gateCapDelaySlew(ceff_, delay, slew); vo_delay_ = delay; gate_slew_ = slew; } @@ -1340,8 +1357,8 @@ findRoot(void (*func)(void *state, double x, double &y, double &dy), // 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). -// Return true if successful. -static bool +// Return error msg on failure. +static const char * newtonRaphson(const int max_iter, double x[], const int size, @@ -1353,20 +1370,19 @@ newtonRaphson(const int max_iter, double **fjac, int *index, double *p, - double *scale, - const char *&error) + double *scale) { for (int k = 0; k < max_iter; k++) { - if (!eval(state)) { - error = "Newton-Raphson eval failed"; - return false; - } + if (!eval(state)) + return "Newton-Raphson eval failed"; for (int i = 0; i < size; i++) // Right-hand side of linear equations. p[i] = -fvec[i]; - const char *lu_error; - if (luDecomp(fjac, size, index, scale, lu_error)) { + const char *error = luDecomp(fjac, size, index, scale); + if (error) + return error; + else { luSolve(fjac, size, index, p); bool all_under_x_tol = true; @@ -1376,15 +1392,10 @@ newtonRaphson(const int max_iter, x[i] += p[i]; } if (all_under_x_tol) - return true; - } - else { - error = lu_error; - return false; + return nullptr; } } - error = "Newton-Raphson max iterations exceeded"; - return false; + return "Newton-Raphson max iterations exceeded"; } // luDecomp, luSolve based on MatClass from C. R. Birchenhall, @@ -1398,15 +1409,14 @@ newtonRaphson(const int max_iter, // // Replaces a[0..size-1][0..size-1] by the LU decomposition. // index[0..size-1] is an output vector of the row permutations. -// Return true if successful. -bool +// Return error msg on failure. +const char * luDecomp(double **a, const int size, int *index, // Temporary supplied by caller. // scale stores the implicit scaling of each row. - double *scale, - const char *&error) + double *scale) { // Find implicit scaling factors. for (int i = 0; i < size; i++) { @@ -1416,10 +1426,8 @@ luDecomp(double **a, if (temp > big) big = temp; } - if (big == 0.0) { - error = "LU decomposition: no non-zero row element"; - return false; - } + if (big == 0.0) + return "LU decomposition: no non-zero row element"; scale[i] = 1.0 / big; } int size_1 = size - 1; @@ -1469,7 +1477,7 @@ luDecomp(double **a, a[i][j] *= pivot; } } - return true; + return nullptr; } // Solves the set of size linear equations a*x=b, assuming A is LU form From c9fd10def94e8b0a88ef2601b50634d2e67cb5b7 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Mon, 2 Nov 2020 17:27:27 -0800 Subject: [PATCH 2/9] dcalc --- dcalc/DmpCeff.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index b212319a..511c3e5c 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -694,14 +694,14 @@ DmpAlg::showVl() void DmpAlg::fail(const char *reason) { - // Allow only failures to be reported with a unique debug flag. + // Report failures with a unique debug flag. if (debug_->check("delay_calc", 1) || debug_->check("delay_calc_dmp", 1)) debug_->print("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s\n", reason, units_->capacitanceUnit()->asString(c2_), units_->resistanceUnit()->asString(rpi_), units_->capacitanceUnit()->asString(c1_), - UNITS_->resistanceUnit()->asString(rd_)); + units_->resistanceUnit()->asString(rd_)); } //////////////////////////////////////////////////////////////// From ce8cf852bfc0459497a362c009fe4edca8ff199d Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 3 Nov 2020 11:35:13 -0800 Subject: [PATCH 3/9] dmp_ceff debug flag --- dcalc/DmpCeff.cc | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 511c3e5c..23f1af69 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -352,11 +352,11 @@ DmpAlg::findDriverParams(double ceff) else { t0_ = x_[DmpParam::t0]; dt_ = x_[DmpParam::dt]; - debugPrint3(debug_, "delay_calc", 3, " t0 = %s dt = %s ceff = %s\n", + debugPrint3(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s\n", units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_), units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); - if (debug_->check("delay_calc", 4)) + if (debug_->check("dmp_ceff", 4)) showVo(); return nullptr; } @@ -587,7 +587,7 @@ DmpAlg::loadDelaySlew(const Pin *, elmore_ = elmore; p3_ = 1.0 / elmore; if (driver_valid_ - && debug_->check("delay_calc", 4)) + && debug_->check("dmp_ceff", 4)) showVl(); // Use the driver thresholds and rely on thresholdAdjust to // convert the delay and slew to the load's thresholds. @@ -694,8 +694,8 @@ DmpAlg::showVl() void DmpAlg::fail(const char *reason) { - // Report failures with a unique debug flag. - if (debug_->check("delay_calc", 1) || debug_->check("delay_calc_dmp", 1)) + // Allow only failures to be reported with a unique debug flag. + if (debug_->check("dmp_ceff", 1) || debug_->check("dmp_ceff_fail", 1)) debug_->print("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s\n", reason, units_->capacitanceUnit()->asString(c2_), @@ -757,7 +757,7 @@ DmpCap::init(const LibertyLibrary *drvr_library, double rpi, double c1) { - debugPrint0(debug_, "delay_calc", 3, "Using DMP cap\n"); + debugPrint0(debug_, "dmp_ceff", 3, "Using DMP cap\n"); DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, related_out_cap, c2, rpi, c1); ceff_ = c1 + c2; @@ -767,7 +767,7 @@ void DmpCap::gateDelaySlew(double &delay, double &slew) { - debugPrint1(debug_, "delay_calc", 3, " ceff = %s\n", + debugPrint1(debug_, "dmp_ceff", 3, " ceff = %s\n", units_->capacitanceUnit()->asString(ceff_)); gateCapDelaySlew(ceff_, delay, slew); gate_slew_ = slew; @@ -899,7 +899,7 @@ DmpPi::init(const LibertyLibrary *drvr_library, double rpi, double c1) { - debugPrint0(debug_, "delay_calc", 3, "Using DMP Pi\n"); + debugPrint0(debug_, "dmp_ceff", 3, "Using DMP Pi\n"); DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, related_out_cap, c2, rpi, c1); @@ -1019,7 +1019,7 @@ DmpPi::evalDmpEqns() fjac_[DmpFunc::y50][DmpParam::dt], fjac_[DmpFunc::y50][DmpParam::ceff]); - if (debug_->check("delay_calc", 4)) { + if (debug_->check("dmp_ceff", 4)) { showX(); showFvec(); showJacobian(); @@ -1118,7 +1118,7 @@ DmpOnePole::evalDmpEqns() fvec_[DmpFunc::y50] = y(t_vth, t0, dt, ceff_) - vth_; fvec_[DmpFunc::y20] = y(t_vl, t0, dt, ceff_) - vl_; - if (debug_->check("delay_calc", 4)) { + if (debug_->check("dmp_ceff", 4)) { showX(); showFvec(); } @@ -1133,7 +1133,7 @@ DmpOnePole::evalDmpEqns() fjac_[DmpFunc::y50][DmpParam::dt], dummy); - if (debug_->check("delay_calc", 4)) { + if (debug_->check("dmp_ceff", 4)) { showJacobian(); debug_->print(".................\n"); } @@ -1209,7 +1209,7 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library, double rpi, double c1) { - debugPrint0(debug_, "delay_calc", 3, "Using DMP C2=0\n"); + debugPrint0(debug_, "dmp_ceff", 3, "Using DMP C2=0\n"); DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, related_out_cap, c2, rpi, c1); ceff_ = c1; @@ -1663,7 +1663,7 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, dmp_alg_ = dmp_pi_; dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, drvr_rf_, rd, in_slew, related_out_cap, c2, rpi, c1); - debugPrint6(debug_, "delay_calc", 3, + debugPrint6(debug_, "dmp_ceff", 3, " DMP in_slew = %s c2 = %s rpi = %s c1 = %s Rd = %s (%s alg)\n", units_->timeUnit()->asString(in_slew), units_->capacitanceUnit()->asString(c2), From a26af433fc8203080eeea8c79410f66a32f47e01 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 4 Nov 2020 09:30:42 -0800 Subject: [PATCH 4/9] dmp rd nan proofing --- dcalc/DmpCeff.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 23f1af69..452b7099 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -1751,16 +1751,16 @@ gateModelRd(const LibertyCell *cell, const Pvt *pvt, bool pocv_enabled) { - float cap1 = static_cast((c1 + c2) * .75); - float cap2 = cap1 * 1.1F; - float in_slew1 = static_cast(in_slew); + float cap1 = (c1 + c2) * .75; + float cap2 = cap1 + 1e-15; ArcDelay d1, d2; Slew s1, s2; - gate_model->gateDelay(cell, pvt, in_slew1, cap1, related_out_cap, pocv_enabled, + gate_model->gateDelay(cell, pvt, in_slew, cap1, related_out_cap, pocv_enabled, d1, s1); - gate_model->gateDelay(cell, pvt, in_slew1, cap2, related_out_cap, pocv_enabled, + gate_model->gateDelay(cell, pvt, in_slew, cap2, related_out_cap, pocv_enabled, d2, s2); - return abs(delayAsFloat(d1) - delayAsFloat(d2)) / (cap2 - cap1); + float rd = abs(delayAsFloat(d1) - delayAsFloat(d2)) / (cap2 - cap1); + return rd; } void From 7023e97bb47560d1108c1f09a0be55352624cc3a Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 4 Nov 2020 10:05:47 -0800 Subject: [PATCH 5/9] dcalc --- dcalc/DmpCeff.cc | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 452b7099..4868ad45 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -779,8 +779,8 @@ DmpCap::loadDelaySlew(const Pin *, ArcDelay &delay, Slew &slew) { - delay = static_cast(elmore); - slew = static_cast(gate_slew_); + delay = elmore; + slew = gate_slew_; } bool @@ -1645,22 +1645,25 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library, double rpi, double c1) { - double rd = gate_model - ? gateModelRd(drvr_cell, gate_model, in_slew, c2, c1, - related_out_cap, pvt, pocv_enabled_) - : 0.0; - // Zero Rd means the table is constant and thus independent of load cap. - if (rd < 1e-2 - // Rpi is small compared to Rd, which makes the load capacitive. - || rpi < rd * 1e-3 - // c1/Rpi can be ignored. - || (c1 == 0.0 || c1 < c2 * 1e-3 || rpi == 0.0)) - dmp_alg_ = dmp_cap_; - else if (c2 < c1 * 1e-3) - dmp_alg_ = dmp_zero_c2_; + double rd = 0.0; + if (gate_model) { + rd = gateModelRd(drvr_cell, gate_model, in_slew, c2, c1, + related_out_cap, pvt, pocv_enabled_); + // Zero Rd means the table is constant and thus independent of load cap. + if (rd < 1e-2 + // Rpi is small compared to Rd, which makes the load capacitive. + || rpi < rd * 1e-3 + // c1/Rpi can be ignored. + || (c1 == 0.0 || c1 < c2 * 1e-3 || rpi == 0.0)) + dmp_alg_ = dmp_cap_; + else if (c2 < c1 * 1e-3) + dmp_alg_ = dmp_zero_c2_; + else + // The full monty. + dmp_alg_ = dmp_pi_; + } else - // The full monty. - dmp_alg_ = dmp_pi_; + dmp_alg_ = dmp_cap_; dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, drvr_rf_, rd, in_slew, related_out_cap, c2, rpi, c1); debugPrint6(debug_, "dmp_ceff", 3, From f1924594de1402b1e184c3374d6ed748d0ab8910 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 4 Nov 2020 12:20:21 -0800 Subject: [PATCH 6/9] dmp flush static casts --- dcalc/DmpCeff.cc | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 4868ad45..60cd9383 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -376,12 +376,8 @@ DmpAlg::gateCapDelaySlew(double ceff, { ArcDelay model_delay; Slew model_slew; - gate_model_->gateDelay(drvr_cell_, pvt_, - static_cast(in_slew_), - static_cast(ceff), - related_out_cap_, - pocv_enabled_, - model_delay, model_slew); + gate_model_->gateDelay(drvr_cell_, pvt_, in_slew_, ceff, related_out_cap_, + pocv_enabled_, model_delay, model_slew); delay = delayAsFloat(model_delay); slew = delayAsFloat(model_slew); } @@ -576,12 +572,12 @@ DmpAlg::loadDelaySlew(const Pin *, { if (elmore == 0.0) { delay = 0.0; - slew = static_cast(gate_slew_); + slew = gate_slew_; } else if (elmore < gate_slew_ * 1e-3) { // Elmore delay is small compared to driver slew. - delay = static_cast(elmore); - slew = static_cast(gate_slew_); + delay = elmore; + slew = gate_slew_; } else { elmore_ = elmore; @@ -610,17 +606,17 @@ DmpAlg::loadDelaySlew(const Pin *, // Only report a problem if the difference is significant. if ((gate_slew_ - slew1) > vth_time_tol * gate_slew_) fail("load slew less than driver slew"); - slew1 = static_cast(gate_slew_); + slew1 = gate_slew_; } - delay = static_cast(delay1); - slew = static_cast(slew1); + delay = delay1; + slew = slew1; } else { // Failed - use elmore delay and driver slew. - delay = static_cast(elmore_); + delay = elmore_; // solve v=1-exp(-t/rc) for t, elmore_slew_factor_ = t(vh) - t(vl) // slew = elmore * (log(vh_) - log(vl_)) - slew = static_cast(gate_slew_ + elmore * elmore_slew_factor_); + slew = gate_slew_ + elmore * elmore_slew_factor_; } } } @@ -1616,8 +1612,8 @@ DmpCeffDelayCalc::gateDelay(const LibertyCell *drvr_cell, c2, rpi, c1); double dmp_gate_delay, dmp_drvr_slew; gateDelaySlew(dmp_gate_delay, dmp_drvr_slew); - gate_delay = static_cast(dmp_gate_delay); - drvr_slew = static_cast(dmp_drvr_slew); + gate_delay = dmp_gate_delay; + drvr_slew = dmp_drvr_slew; } else { LumpedCapDelayCalc::gateDelay(drvr_cell, arc, in_slew, load_cap, @@ -1692,7 +1688,7 @@ DmpCeffDelayCalc::ceff(const LibertyCell *drvr_cell, drvr_parasitic, related_out_cap, pvt, dcalc_ap, gate_delay, drvr_slew); if (dmp_alg_) - return static_cast(dmp_alg_->ceff()); + return dmp_alg_->ceff(); else return load_cap; } From 4c3e0ec1bd4b07b5c5f47533f5bacc2e46ca55ce Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 4 Nov 2020 13:39:36 -0800 Subject: [PATCH 7/9] dcalc use try/catch --- dcalc/DmpCeff.cc | 263 ++++++++++++++++++++++------------------------- 1 file changed, 121 insertions(+), 142 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 60cd9383..3468d836 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -66,6 +66,17 @@ enum DmpFunc { y20, y50, ipi }; static const char *dmp_func_index_strings[] = {"y20", "y50", "Ipi"}; +class DmpError : public Exception +{ +public: + DmpError(const char *what); + virtual ~DmpError() {} + virtual const char *what() const noexcept { return what_; } + +private: + const char *what_; +}; + static double gateModelRd(const LibertyCell *cell, GateTableModel *gate_model, @@ -75,7 +86,7 @@ gateModelRd(const LibertyCell *cell, float related_out_cap, const Pvt *pvt, bool pocv_enabled); -static bool +static void evalDmpEqnsState(void *state); static void evalVoEqns(void *state, @@ -94,16 +105,15 @@ findRoot(void (*func)(void *state, double x, double &y, double &dy), double x1, double x2, double x_tol, - int max_iter, - const char *&error); -static const char * + int max_iter); +static void newtonRaphson(const int max_iter, double x[], const int n, const double x_tol, // eval(state) is called to fill fvec and fjac. // Returns false if fails. - bool (*eval)(void *state), + void (*eval)(void *state), void *state, // Temporaries supplied by caller. double *fvec, @@ -116,7 +126,7 @@ luSolve(double **a, const int size, const int *index, double b[]); -static const char * +static void luDecomp(double **a, const int size, int *index, @@ -154,7 +164,7 @@ public: // 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 bool evalDmpEqns() = 0; + virtual void evalDmpEqns() = 0; // Output response to vs(t) ramp driving pi model load. double vo(double t); double dVoDt(double t); @@ -165,7 +175,7 @@ public: protected: // Find driver parameters t0, delta_t, Ceff. - const char *findDriverParams(double ceff); + void findDriverParams(double ceff); void gateCapDelaySlew(double cl, double &delay, double &slew); @@ -189,13 +199,11 @@ protected: void showX(); void showFvec(); void showJacobian(); - const char *findDriverDelaySlew(double &delay, - double &slew); - double findVoCrossing(double vth, - const char *&error); + void findDriverDelaySlew(double &delay, + double &slew); + double findVoCrossing(double vth); void showVo(); - bool findVlCrossing(double vth, - double &t); + double findVlCrossing(double vth); void showVl(); void fail(const char *reason); @@ -334,7 +342,7 @@ DmpAlg::init(const LibertyLibrary *drvr_library, // Find Ceff, delta_t and t0 for the driver. // Return error msg on failure. -const char * +void DmpAlg::findDriverParams(double ceff) { x_[DmpParam::ceff] = ceff; @@ -344,29 +352,23 @@ DmpAlg::findDriverParams(double ceff) double t0 = t_vth + log(1.0 - vth_) * rd_ * ceff - vth_ * dt; x_[DmpParam::dt] = dt; x_[DmpParam::t0] = t0; - const char *error = newtonRaphson(100, x_, nr_order_, driver_param_tol, - evalDmpEqnsState, - this, fvec_, fjac_, index_, p_, scale_); - if (error) - return error; - else { - t0_ = x_[DmpParam::t0]; - dt_ = x_[DmpParam::dt]; - debugPrint3(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s\n", - units_->timeUnit()->asString(t0_), - units_->timeUnit()->asString(dt_), - units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); - if (debug_->check("dmp_ceff", 4)) - showVo(); - return nullptr; - } + newtonRaphson(100, x_, nr_order_, driver_param_tol, evalDmpEqnsState, + this, fvec_, fjac_, index_, p_, scale_); + t0_ = x_[DmpParam::t0]; + dt_ = x_[DmpParam::dt]; + debugPrint3(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s\n", + units_->timeUnit()->asString(t0_), + units_->timeUnit()->asString(dt_), + units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); + if (debug_->check("dmp_ceff", 4)) + showVo(); } -static bool +static void evalDmpEqnsState(void *state) { DmpAlg *alg = reinterpret_cast(state); - return alg->evalDmpEqns(); + alg->evalDmpEqns(); } void @@ -427,12 +429,9 @@ DmpAlg::dy(double t, double &dydcl) { double t1 = t - t0; -#if 0 if (t1 <= 0.0) dydt0 = dyddt = dydcl = 0.0; - else -#endif -if (t1 <= dt) { + else if (t1 <= dt) { dydt0 = -y0dt(t1, cl) / dt; dyddt = -y0(t1, cl) / (dt * dt); dydcl = y0dcl(t1, cl) / dt; @@ -488,34 +487,24 @@ DmpAlg::showJacobian() } } -// Return error msg on failure. -const char * +void DmpAlg::findDriverDelaySlew(double &delay, double &slew) { const char *error = nullptr; - delay = findVoCrossing(vth_, error); - if (error) - return error; - double tl = findVoCrossing(vl_, error); - if (error) - return error; - double th = findVoCrossing(vh_, error); - if (error) - return error; + delay = findVoCrossing(vth_); + double tl = findVoCrossing(vl_); + double th = findVoCrossing(vh_); slew = (th - tl) / slew_derate_; - return nullptr; } // Find t such that vo(t)=v. -// Return true if successful. double -DmpAlg::findVoCrossing(double vth, - const char *&error) +DmpAlg::findVoCrossing(double vth) { v_cross_ = vth; double ub = voCrossingUpperBound(); - return findRoot(evalVoEqns, this, t0_, ub, vth_time_tol, find_root_max_iter, error); + return findRoot(evalVoEqns, this, t0_, ub, vth_time_tol, find_root_max_iter); } static void @@ -570,28 +559,27 @@ DmpAlg::loadDelaySlew(const Pin *, ArcDelay &delay, Slew &slew) { - if (elmore == 0.0) { - delay = 0.0; - slew = gate_slew_; - } - else if (elmore < gate_slew_ * 1e-3) { - // Elmore delay is small compared to driver slew. + if (!driver_valid_ + || elmore == 0.0 + // Elmore delay is small compared to driver slew. + || elmore < gate_slew_ * 1e-3) { delay = elmore; + // solve v=1-exp(-t/rc) for t, elmore_slew_factor_ = t(vh) - t(vl) + // slew = elmore * (log(vh_) - log(vl_)) + // slew = elmore * elmore_slew_factor_; slew = gate_slew_; } else { - elmore_ = elmore; - p3_ = 1.0 / elmore; - if (driver_valid_ - && debug_->check("dmp_ceff", 4)) - showVl(); // Use the driver thresholds and rely on thresholdAdjust to // convert the delay and slew to the load's thresholds. - double tl, th, load_delay; - if (driver_valid_ - && findVlCrossing(vth_, load_delay) - && findVlCrossing(vl_, tl) - && findVlCrossing(vh_, th)) { + try { + if (debug_->check("dmp_ceff", 4)) + showVl(); + elmore_ = elmore; + p3_ = 1.0 / elmore; + double load_delay = findVlCrossing(vth_); + double tl = findVlCrossing(vl_); + double th = findVlCrossing(vh_); // Measure delay from Vo, the load dependent source excitation. double delay1 = load_delay - vo_delay_; double slew1 = (th - tl) / slew_derate_; @@ -600,7 +588,7 @@ DmpAlg::loadDelaySlew(const Pin *, if (-delay1 > vth_time_tol * vo_delay_) fail("load delay less than zero"); // Use elmore delay. - delay1 = 1.0 / p3_; + delay1 = elmore; } if (slew1 < gate_slew_) { // Only report a problem if the difference is significant. @@ -611,30 +599,24 @@ DmpAlg::loadDelaySlew(const Pin *, delay = delay1; slew = slew1; } - else { + catch (DmpError &error) { // Failed - use elmore delay and driver slew. delay = elmore_; // solve v=1-exp(-t/rc) for t, elmore_slew_factor_ = t(vh) - t(vl) // slew = elmore * (log(vh_) - log(vl_)) - slew = gate_slew_ + elmore * elmore_slew_factor_; + slew = elmore * elmore_slew_factor_; } } } // Find t such that vl(t)=v. // Return true if successful. -bool -DmpAlg::findVlCrossing(double vth, - double &t) +double +DmpAlg::findVlCrossing(double vth) { v_cross_ = vth; double ub = vlCrossingUpperBound(); - const char *error; - t = findRoot(evalVlEqns, this, t0_, ub, vth_time_tol, find_root_max_iter, - error); - if (error) - fail("findVlCrossing: Vl(t) did not cross threshold"); - return (error == nullptr); + return findRoot(evalVlEqns, this, t0_, ub, vth_time_tol, find_root_max_iter); } double @@ -725,7 +707,7 @@ public: double elmore, ArcDelay &delay, Slew &slew); - virtual bool evalDmpEqns(); + virtual void evalDmpEqns(); virtual double voCrossingUpperBound(); private: @@ -779,10 +761,9 @@ DmpCap::loadDelaySlew(const Pin *, slew = gate_slew_; } -bool +void DmpCap::evalDmpEqns() { - return true; } double @@ -836,11 +817,11 @@ public: double c1); virtual void gateDelaySlew(double &delay, double &slew); - virtual bool evalDmpEqns(); + virtual void evalDmpEqns(); virtual double voCrossingUpperBound(); private: - const char *findDriverParamsPi(); + void findDriverParamsPi(); virtual double v0(double t); virtual double dv0dt(double t); double ipiIceff(double t0, @@ -924,14 +905,8 @@ void DmpPi::gateDelaySlew(double &delay, double &slew) { - const char *error = findDriverParamsPi(); - if (error) { - fail(error); - // Driver calculation failed - use Ceff=c1+c2. - ceff_ = c1_ + c2_; - gateCapDelaySlew(ceff_, delay, slew); - } - else { + try { + findDriverParamsPi(); ceff_ = x_[DmpParam::ceff]; driver_valid_ = true; double table_slew; @@ -941,38 +916,50 @@ DmpPi::gateDelaySlew(double &delay, // Vo slew is more accurate than table // (-8% max, -3% avg vs -32% max, -12% avg). // Need Vo delay to measure load wire delay waveform. - const char *error = findDriverDelaySlew(vo_delay_, slew); - if (error) { - fail(error); + try { + findDriverDelaySlew(vo_delay_, slew); + } + catch (DmpError &error) { + fail(error.what()); // Fall back to table slew. slew = table_slew; } } + catch (DmpError &error) { + fail(error.what()); + // Driver calculation failed - use Ceff=c1+c2. + driver_valid_ = false; + ceff_ = c1_ + c2_; + gateCapDelaySlew(ceff_, delay, slew); + } // Save for wire delay calc. gate_slew_ = slew; } -const char * +void DmpPi::findDriverParamsPi() { - const char *error; - error = findDriverParams(c2_ + c1_); - if (error) - error = findDriverParams(c2_); - return error; + try { + findDriverParams(c2_ + c1_); + } + catch (DmpError &) { + findDriverParams(c2_); + } } // Given x_ as a vector of input parameters, fill fvec_ with the // equations evaluated at x_ and fjac_ with the jacobian evaluated at x_. -bool +void DmpPi::evalDmpEqns() { double t0 = x_[DmpParam::t0]; double dt = x_[DmpParam::dt]; double ceff = x_[DmpParam::ceff]; - if (ceff > (c1_ + c2_) || ceff < 0.0) - return false; + if (ceff < 0.0) + throw DmpError("eqn eval failed: ceff < 0"); + if (ceff > (c1_ + c2_)) + throw DmpError("eqn eval failed: ceff > c2 + c1"); double t_vth, t_vl, slew; gateDelays(ceff, t_vth, t_vl, slew); @@ -981,7 +968,7 @@ DmpPi::evalDmpEqns() ceff_time = 1.4 * dt; if (dt <= 0.0) - return false; + throw DmpError("eqn eval failed: dt < 0"); double exp_p1_dt = exp(-p1_ * dt); double exp_p2_dt = exp(-p2_ * dt); @@ -1021,7 +1008,6 @@ DmpPi::evalDmpEqns() showJacobian(); debug_->print(".................\n"); } - return true; } // Eqn 13, Eqn 14. @@ -1090,7 +1076,7 @@ class DmpOnePole : public DmpAlg { public: DmpOnePole(StaState *sta); - virtual bool evalDmpEqns(); + virtual void evalDmpEqns(); virtual double voCrossingUpperBound(); }; @@ -1099,7 +1085,7 @@ DmpOnePole::DmpOnePole(StaState *sta) : { } -bool +void DmpOnePole::evalDmpEqns() { double t0 = x_[DmpParam::t0]; @@ -1133,7 +1119,6 @@ DmpOnePole::evalDmpEqns() showJacobian(); debug_->print(".................\n"); } - return true; } double @@ -1291,19 +1276,15 @@ findRoot(void (*func)(void *state, double x, double &y, double &dy), double x1, double x2, double x_tol, - int max_iter, - const char *&error) + 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)) { - error = "findRoot: initial bounds do not surround a root"; - return 0.0; - } + if ((y1 > 0.0 && y2 > 0.0) || (y1 < 0.0 && y2 < 0.0)) + throw DmpError("findRoot: initial bounds do not surround a root"); - error = nullptr; if (y1 == 0.0) return x1; @@ -1346,20 +1327,19 @@ findRoot(void (*func)(void *state, double x, double &y, double &dy), else x2 = root; } - error = "findRoot: max iterations exceeded"; - return 0.0; + 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). // Return error msg on failure. -static const char * +static void newtonRaphson(const int max_iter, double x[], const int size, const double x_tol, - bool (*eval)(void *state), + void (*eval)(void *state), void *state, // Temporaries supplied by caller. double *fvec, @@ -1369,29 +1349,23 @@ newtonRaphson(const int max_iter, double *scale) { for (int k = 0; k < max_iter; k++) { - if (!eval(state)) - return "Newton-Raphson eval failed"; - + eval(state); for (int i = 0; i < size; i++) // Right-hand side of linear equations. p[i] = -fvec[i]; - const char *error = luDecomp(fjac, size, index, scale); - if (error) - return error; - else { - luSolve(fjac, size, index, p); + luDecomp(fjac, size, index, scale); + luSolve(fjac, size, index, p); - bool all_under_x_tol = true; - for (int i = 0; i < size; i++) { - if (abs(p[i]) > abs(x[i]) * x_tol) - all_under_x_tol = false; - x[i] += p[i]; - } - if (all_under_x_tol) - return nullptr; + bool all_under_x_tol = true; + for (int i = 0; i < size; i++) { + if (abs(p[i]) > abs(x[i]) * x_tol) + all_under_x_tol = false; + x[i] += p[i]; } + if (all_under_x_tol) + return; } - return "Newton-Raphson max iterations exceeded"; + throw DmpError("Newton-Raphson max iterations exceeded"); } // luDecomp, luSolve based on MatClass from C. R. Birchenhall, @@ -1406,7 +1380,7 @@ newtonRaphson(const int max_iter, // Replaces a[0..size-1][0..size-1] by the LU decomposition. // index[0..size-1] is an output vector of the row permutations. // Return error msg on failure. -const char * +void luDecomp(double **a, const int size, int *index, @@ -1423,7 +1397,7 @@ luDecomp(double **a, big = temp; } if (big == 0.0) - return "LU decomposition: no non-zero row element"; + throw DmpError("LU decomposition: no non-zero row element"); scale[i] = 1.0 / big; } int size_1 = size - 1; @@ -1473,7 +1447,6 @@ luDecomp(double **a, a[i][j] *= pivot; } } - return nullptr; } // Solves the set of size linear equations a*x=b, assuming A is LU form @@ -1789,4 +1762,10 @@ DmpCeffDelayCalc::copyState(const StaState *sta) dmp_zero_c2_->copyState(sta); } +DmpError::DmpError(const char *what) : + what_(what) +{ + //printf("DmpError %s\n", what); +} + } // namespace From 6136412cb37127b18a0943620b44c831211ad626 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 4 Nov 2020 14:01:22 -0800 Subject: [PATCH 8/9] dcalc load slew default --- dcalc/DmpCeff.cc | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index 3468d836..fec6e609 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -268,7 +268,6 @@ protected: // Load rspf elmore delay. double elmore_; double p3_; - double elmore_slew_factor_; private: virtual double dvl0dt(double t) = 0; @@ -336,8 +335,6 @@ DmpAlg::init(const LibertyLibrary *drvr_library, vl_ = drvr_library->slewLowerThreshold(rf); vh_ = drvr_library->slewUpperThreshold(rf); slew_derate_ = drvr_library->slewDerateFromLibrary(); - elmore_slew_factor_ = log(vh_) - log(vl_); - } // Find Ceff, delta_t and t0 for the driver. @@ -564,9 +561,6 @@ DmpAlg::loadDelaySlew(const Pin *, // Elmore delay is small compared to driver slew. || elmore < gate_slew_ * 1e-3) { delay = elmore; - // solve v=1-exp(-t/rc) for t, elmore_slew_factor_ = t(vh) - t(vl) - // slew = elmore * (log(vh_) - log(vl_)) - // slew = elmore * elmore_slew_factor_; slew = gate_slew_; } else { @@ -600,11 +594,8 @@ DmpAlg::loadDelaySlew(const Pin *, slew = slew1; } catch (DmpError &error) { - // Failed - use elmore delay and driver slew. delay = elmore_; - // solve v=1-exp(-t/rc) for t, elmore_slew_factor_ = t(vh) - t(vl) - // slew = elmore * (log(vh_) - log(vl_)) - slew = elmore * elmore_slew_factor_; + slew = gate_slew_; } } } From 7309bb681100f9085f2b62f35ed9b7a1c1546ca7 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Wed, 4 Nov 2020 14:33:11 -0800 Subject: [PATCH 9/9] DmpZeroC2 --- dcalc/DmpCeff.cc | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/dcalc/DmpCeff.cc b/dcalc/DmpCeff.cc index fec6e609..3365e0ab 100644 --- a/dcalc/DmpCeff.cc +++ b/dcalc/DmpCeff.cc @@ -1199,25 +1199,21 @@ void DmpZeroC2::gateDelaySlew(double &delay, double &slew) { -#if 0 - // Not converging to a reasonable solution. set_load10 - const char *error = findDriverParams(c1_); - ceff_ = c1_; - driver_valid_ = (error == nullptr); - if (error == nullptr) - error = findDriverDelaySlew(delay, slew); - if (error) { - fail(error); + try { + findDriverParams(c1_); + ceff_ = c1_; + driver_valid_ = true; + findDriverDelaySlew(delay, slew); + } + catch (DmpError &error) { + fail(error.what()); // Fall back to table slew. + driver_valid_ = false; + ceff_ = c1_; gateCapDelaySlew(ceff_, delay, slew); } vo_delay_ = delay; gate_slew_ = slew; -#endif - ceff_ = c1_; - gateCapDelaySlew(ceff_, delay, slew); - vo_delay_ = delay; - gate_slew_ = slew; } double