upstream changes plus resolved conflict in ci.yml
Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
This commit is contained in:
commit
0c96ee9a22
|
|
@ -34,6 +34,13 @@ alwaysApply: true
|
|||
- Prefer `std::string` over `char*` for string members.
|
||||
- Prefer pass-by-value and move for sink parameters (parameters that get stored).
|
||||
|
||||
## Control flow
|
||||
|
||||
- Prefer positive `if` conditions that wrap the main logic instead of early
|
||||
`continue` or `return` to skip work.
|
||||
- Example: use `if (!visited.contains(vertex)) { ... }` rather than
|
||||
`if (visited.contains(vertex)) continue;`.
|
||||
|
||||
## File Extensions
|
||||
|
||||
- C++ source: `.cc`
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
|
|
|
|||
350
dcalc/DmpCeff.cc
350
dcalc/DmpCeff.cc
|
|
@ -31,6 +31,7 @@
|
|||
// slew voltage is matched instead of y20 in eqn 12.
|
||||
|
||||
#include "DmpCeff.hh"
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
|
@ -137,20 +138,23 @@ DmpAlg::init(const LibertyLibrary *drvr_library,
|
|||
void
|
||||
DmpAlg::findDriverParams(double ceff)
|
||||
{
|
||||
Eigen::Vector3d x = Eigen::Vector3d::Zero();
|
||||
if (nr_order_ == 3)
|
||||
x_[DmpParam::ceff] = ceff;
|
||||
x[DmpParam::ceff] = ceff;
|
||||
auto [t_vth, t_vl, slew] = gateDelays(ceff);
|
||||
// Scale slew to 0-100%
|
||||
double dt = slew / (vh_ - vl_);
|
||||
double t0 = t_vth + std::log(1.0 - vth_) * rd_ * ceff - vth_ * dt;
|
||||
x_[DmpParam::dt] = dt;
|
||||
x_[DmpParam::t0] = t0;
|
||||
newtonRaphson();
|
||||
t0_ = x_[DmpParam::t0];
|
||||
dt_ = x_[DmpParam::dt];
|
||||
x[DmpParam::dt] = dt;
|
||||
x[DmpParam::t0] = t0;
|
||||
newtonRaphson(x);
|
||||
t0_ = x[DmpParam::t0];
|
||||
dt_ = x[DmpParam::dt];
|
||||
if (nr_order_ == 3)
|
||||
ceff_ = x[DmpParam::ceff];
|
||||
debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}",
|
||||
units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_),
|
||||
units_->capacitanceUnit()->asString(x_[DmpParam::ceff]));
|
||||
units_->capacitanceUnit()->asString(ceff_));
|
||||
if (debug_->check("dmp_ceff", 4))
|
||||
showVo();
|
||||
}
|
||||
|
|
@ -241,21 +245,21 @@ DmpAlg::y0dcl(double t,
|
|||
}
|
||||
|
||||
void
|
||||
DmpAlg::showX()
|
||||
DmpAlg::showX(const Eigen::Vector3d &x)
|
||||
{
|
||||
for (int i = 0; i < nr_order_; i++)
|
||||
report_->report("{:4} {:12.3e}", dmp_param_index_strings[i], x_[i]);
|
||||
report_->report("{:4} {:12.3e}", dmp_param_index_strings[i], x[i]);
|
||||
}
|
||||
|
||||
void
|
||||
DmpAlg::showFvec()
|
||||
DmpAlg::showFvec(const Eigen::Vector3d &fvec)
|
||||
{
|
||||
for (int i = 0; i < nr_order_; i++)
|
||||
report_->report("{:4} {:12.3e}", dmp_func_index_strings[i], fvec_[i]);
|
||||
report_->report("{:4} {:12.3e}", dmp_func_index_strings[i], fvec[i]);
|
||||
}
|
||||
|
||||
void
|
||||
DmpAlg::showJacobian()
|
||||
DmpAlg::showJacobian(const Eigen::Matrix3d &fjac)
|
||||
{
|
||||
std::string line = " ";
|
||||
for (int j = 0; j < nr_order_; j++)
|
||||
|
|
@ -265,7 +269,7 @@ DmpAlg::showJacobian()
|
|||
line.clear();
|
||||
line += sta::format("{:4} ", dmp_func_index_strings[i]);
|
||||
for (int j = 0; j < nr_order_; j++)
|
||||
line += sta::format("{:12.3e} ", fjac_[i][j]);
|
||||
line += sta::format("{:12.3e} ", fjac(i, j));
|
||||
report_->reportLine(line);
|
||||
}
|
||||
}
|
||||
|
|
@ -505,7 +509,9 @@ DmpCap::loadDelaySlew(const Pin *,
|
|||
}
|
||||
|
||||
void
|
||||
DmpCap::evalDmpEqns()
|
||||
DmpCap::evalDmpEqns(Eigen::Vector3d &,
|
||||
Eigen::Vector3d &,
|
||||
Eigen::Matrix3d &)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -584,7 +590,6 @@ DmpPi::gateDelaySlew()
|
|||
double slew = 0.0;
|
||||
try {
|
||||
findDriverParamsPi();
|
||||
ceff_ = x_[DmpParam::ceff];
|
||||
auto [table_delay, table_slew] = gateCapDelaySlew(ceff_);
|
||||
delay = table_delay;
|
||||
// slew = table_slew;
|
||||
|
|
@ -623,60 +628,85 @@ DmpPi::findDriverParamsPi()
|
|||
// Given x_ as a vector of input parameters, fill fvec_ with the
|
||||
// equations evaluated at x_ and fjac_ with the jacobian evaluated at x_.
|
||||
void
|
||||
DmpPi::evalDmpEqns()
|
||||
DmpPi::evalDmpEqns(Eigen::Vector3d &x,
|
||||
Eigen::Vector3d &fvec,
|
||||
Eigen::Matrix3d &fjac)
|
||||
{
|
||||
double t0 = x_[DmpParam::t0];
|
||||
double dt = x_[DmpParam::dt];
|
||||
double ceff = x_[DmpParam::ceff];
|
||||
const double t0 = x[DmpParam::t0];
|
||||
const double dt = x[DmpParam::dt];
|
||||
const double ceff = x[DmpParam::ceff];
|
||||
|
||||
if (ceff < 0.0)
|
||||
// Validate bounds to prevent mathematical domain errors.
|
||||
if (ceff < 0.0) {
|
||||
throw DmpError("eqn eval failed: ceff < 0");
|
||||
if (ceff > (c1_ + c2_))
|
||||
}
|
||||
if (ceff > (c1_ + c2_)) {
|
||||
throw DmpError("eqn eval failed: ceff > c2 + c1");
|
||||
}
|
||||
if (dt <= 0.0) {
|
||||
throw DmpError("eqn eval failed: dt < 0");
|
||||
}
|
||||
|
||||
auto [t_vth, t_vl, slew] = gateDelays(ceff);
|
||||
if (slew == 0.0)
|
||||
if (slew == 0.0) {
|
||||
throw DmpError("eqn eval failed: slew = 0");
|
||||
}
|
||||
|
||||
double ceff_time = slew / (vh_ - vl_);
|
||||
ceff_time = std::min(ceff_time, 1.4 * dt);
|
||||
// ceff_time is bounded by 1.4 * dt.
|
||||
const double ceff_time = std::min(slew / (vh_ - vl_), 1.4 * dt);
|
||||
|
||||
if (dt <= 0.0)
|
||||
throw DmpError("eqn eval failed: dt < 0");
|
||||
// Pre-calculate exponential terms to avoid redundant calls to
|
||||
// transcendental functions.
|
||||
const double exp_p1_dt = exp2(-p1_ * dt);
|
||||
const double exp_p2_dt = exp2(-p2_ * dt);
|
||||
const double exp_dt_rd_ceff = exp2(-dt / (rd_ * ceff));
|
||||
|
||||
double exp_p1_dt = exp2(-p1_ * dt);
|
||||
double exp_p2_dt = exp2(-p2_ * dt);
|
||||
double exp_dt_rd_ceff = exp2(-dt / (rd_ * ceff));
|
||||
// Evaluate function values (residuals).
|
||||
const double y50 = y(t_vth, t0, dt, ceff).first;
|
||||
const double y20 = y(t_vl, t0, dt, ceff).first;
|
||||
|
||||
double y50 = y(t_vth, t0, dt, ceff).first;
|
||||
// Match Vl.
|
||||
double y20 = y(t_vl, t0, dt, ceff).first;
|
||||
fvec_[DmpFunc::ipi] = ipiIceff(t0, dt, ceff_time, ceff);
|
||||
fvec_[DmpFunc::y50] = y50 - vth_;
|
||||
fvec_[DmpFunc::y20] = y20 - vl_;
|
||||
fjac_[DmpFunc::ipi][DmpParam::t0] = 0.0;
|
||||
fjac_[DmpFunc::ipi][DmpParam::dt] =
|
||||
(-A_ * dt + B_ * dt * exp_p1_dt - (2 * B_ / p1_) * (1.0 - exp_p1_dt)
|
||||
+ D_ * dt * exp_p2_dt - (2 * D_ / p2_) * (1.0 - exp_p2_dt)
|
||||
+ rd_ * ceff
|
||||
* (dt + dt * exp_dt_rd_ceff - 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff)))
|
||||
/ (rd_ * dt * dt * dt);
|
||||
fjac_[DmpFunc::ipi][DmpParam::ceff] =
|
||||
(2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff)))
|
||||
/ (dt * dt);
|
||||
fvec[DmpFunc::ipi] = ipiIceff(t0, dt, ceff_time, ceff);
|
||||
fvec[DmpFunc::y50] = y50 - vth_;
|
||||
fvec[DmpFunc::y20] = y20 - vl_;
|
||||
|
||||
std::tie(fjac_[DmpFunc::y20][DmpParam::t0],
|
||||
fjac_[DmpFunc::y20][DmpParam::dt],
|
||||
fjac_[DmpFunc::y20][DmpParam::ceff]) = dy(t_vl, t0, dt, ceff);
|
||||
// Pre-calculate common sub-expressions for the Jacobian derivatives.
|
||||
const double b_div_p1 = B_ / p1_;
|
||||
const double d_div_p2 = D_ / p2_;
|
||||
const double rd_ceff = rd_ * ceff;
|
||||
|
||||
std::tie(fjac_[DmpFunc::y50][DmpParam::t0],
|
||||
fjac_[DmpFunc::y50][DmpParam::dt],
|
||||
fjac_[DmpFunc::y50][DmpParam::ceff]) = dy(t_vth, t0, dt, ceff);
|
||||
// Row 1 (Ipi derivatives).
|
||||
fjac(DmpFunc::ipi, DmpParam::t0) = 0.0;
|
||||
|
||||
// Derivative w.r.t dt (broken down into physical terms).
|
||||
const double term_a = -A_ * dt;
|
||||
const double term_b =
|
||||
B_ * dt * exp_p1_dt - 2.0 * b_div_p1 * (1.0 - exp_p1_dt);
|
||||
const double term_d =
|
||||
D_ * dt * exp_p2_dt - 2.0 * d_div_p2 * (1.0 - exp_p2_dt);
|
||||
const double term_rd = rd_ceff
|
||||
* (dt + dt * exp_dt_rd_ceff - 2.0 * rd_ceff * (1.0 - exp_dt_rd_ceff));
|
||||
|
||||
fjac(DmpFunc::ipi, DmpParam::dt) =
|
||||
(term_a + term_b + term_d + term_rd) / (rd_ * dt * dt * dt);
|
||||
|
||||
// Derivative w.r.t ceff (reusing exp_dt_rd_ceff).
|
||||
const double two_rd_ceff = 2.0 * rd_ceff;
|
||||
fjac(DmpFunc::ipi, DmpParam::ceff) =
|
||||
(two_rd_ceff - dt - (two_rd_ceff + dt) * exp_dt_rd_ceff) / (dt * dt);
|
||||
|
||||
// Rows 2 & 3 (y20 and y50 derivatives).
|
||||
std::tie(fjac(DmpFunc::y20, DmpParam::t0),
|
||||
fjac(DmpFunc::y20, DmpParam::dt),
|
||||
fjac(DmpFunc::y20, DmpParam::ceff)) = dy(t_vl, t0, dt, ceff);
|
||||
|
||||
std::tie(fjac(DmpFunc::y50, DmpParam::t0),
|
||||
fjac(DmpFunc::y50, DmpParam::dt),
|
||||
fjac(DmpFunc::y50, DmpParam::ceff)) = dy(t_vth, t0, dt, ceff);
|
||||
|
||||
if (debug_->check("dmp_ceff", 4)) {
|
||||
showX();
|
||||
showFvec();
|
||||
showJacobian();
|
||||
showX(x);
|
||||
showFvec(fvec);
|
||||
showJacobian(fjac);
|
||||
report_->report(".................");
|
||||
}
|
||||
}
|
||||
|
|
@ -741,35 +771,37 @@ DmpOnePole::DmpOnePole(StaState *sta) :
|
|||
}
|
||||
|
||||
void
|
||||
DmpOnePole::evalDmpEqns()
|
||||
DmpOnePole::evalDmpEqns(Eigen::Vector3d &x,
|
||||
Eigen::Vector3d &fvec,
|
||||
Eigen::Matrix3d &fjac)
|
||||
{
|
||||
double t0 = x_[DmpParam::t0];
|
||||
double dt = x_[DmpParam::dt];
|
||||
double t0 = x[DmpParam::t0];
|
||||
double dt = x[DmpParam::dt];
|
||||
|
||||
auto [t_vth, t_vl, ignore1] = gateDelays(ceff_);
|
||||
double ignore2;
|
||||
|
||||
if (dt <= 0.0)
|
||||
dt = x_[DmpParam::dt] = (t_vl - t_vth) / 100;
|
||||
dt = x[DmpParam::dt] = (t_vl - t_vth) / 100;
|
||||
|
||||
fvec_[DmpFunc::y50] = y(t_vth, t0, dt, ceff_).first - vth_;
|
||||
fvec_[DmpFunc::y20] = y(t_vl, t0, dt, ceff_).first - vl_;
|
||||
fvec[DmpFunc::y50] = y(t_vth, t0, dt, ceff_).first - vth_;
|
||||
fvec[DmpFunc::y20] = y(t_vl, t0, dt, ceff_).first - vl_;
|
||||
|
||||
if (debug_->check("dmp_ceff", 4)) {
|
||||
showX();
|
||||
showFvec();
|
||||
showX(x);
|
||||
showFvec(fvec);
|
||||
}
|
||||
|
||||
std::tie(fjac_[DmpFunc::y20][DmpParam::t0],
|
||||
fjac_[DmpFunc::y20][DmpParam::dt],
|
||||
std::tie(fjac(DmpFunc::y20, DmpParam::t0),
|
||||
fjac(DmpFunc::y20, DmpParam::dt),
|
||||
ignore2) = dy(t_vl, t0, dt, ceff_);
|
||||
|
||||
std::tie(fjac_[DmpFunc::y50][DmpParam::t0],
|
||||
fjac_[DmpFunc::y50][DmpParam::dt],
|
||||
std::tie(fjac(DmpFunc::y50, DmpParam::t0),
|
||||
fjac(DmpFunc::y50, DmpParam::dt),
|
||||
ignore2) = dy(t_vth, t0, dt, ceff_);
|
||||
|
||||
if (debug_->check("dmp_ceff", 4)) {
|
||||
showJacobian();
|
||||
showJacobian(fjac);
|
||||
report_->report(".................");
|
||||
}
|
||||
}
|
||||
|
|
@ -870,136 +902,86 @@ DmpZeroC2::voCrossingUpperBound()
|
|||
// driver_param_tol_ is the scale that all changes in x must be under (1.0 = 100%).
|
||||
// evalDmpEqns() fills fvec_ and fjac_.
|
||||
void
|
||||
DmpAlg::newtonRaphson()
|
||||
DmpAlg::newtonRaphson(Eigen::Vector3d &x)
|
||||
{
|
||||
for (int k = 0; k < newton_raphson_max_iter_; k++) {
|
||||
evalDmpEqns();
|
||||
for (int i = 0; i < nr_order_; i++)
|
||||
// Right-hand side of linear equations.
|
||||
p_[i] = -fvec_[i];
|
||||
luDecomp();
|
||||
luSolve();
|
||||
Eigen::Vector3d fvec = Eigen::Vector3d::Zero();
|
||||
Eigen::Matrix3d fjac = Eigen::Matrix3d::Zero();
|
||||
Eigen::Vector3d p = Eigen::Vector3d::Zero();
|
||||
|
||||
for (int k = 0; k < newton_raphson_max_iter_; k++) {
|
||||
evalDmpEqns(x, fvec, fjac);
|
||||
|
||||
p = solveNewtonStep(fjac, fvec);
|
||||
|
||||
// Note: 'auto' on Eigen expressions captures the expression template
|
||||
// and doesn't form a temporary vector/matrix, avoiding extra
|
||||
// allocations.
|
||||
auto p_abs = p.head(nr_order_).array().abs();
|
||||
auto x_tol = x.head(nr_order_).array().abs() * driver_param_tol_;
|
||||
bool all_under_x_tol = (p_abs <= x_tol).all();
|
||||
x.head(nr_order_) += p.head(nr_order_);
|
||||
|
||||
bool all_under_x_tol = true;
|
||||
for (int i = 0; i < nr_order_; i++) {
|
||||
if (std::abs(p_[i]) > std::abs(x_[i]) * driver_param_tol_)
|
||||
all_under_x_tol = false;
|
||||
x_[i] += p_[i];
|
||||
}
|
||||
if (all_under_x_tol) {
|
||||
evalDmpEqns();
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw DmpError("Newton-Raphson max iterations exceeded");
|
||||
}
|
||||
|
||||
// luDecomp, luSolve based on MatClass from C. R. Birchenhall,
|
||||
// University of Manchester
|
||||
// ftp://ftp.mcc.ac.uk/pub/matclass/libmat.tar.Z
|
||||
|
||||
// Crout's Method of LU decomposition of square matrix, with implicit
|
||||
// partial pivoting. fjac_ is overwritten. U is explicit in the upper
|
||||
// triangle and L is in multiplier form in the subdiagionals i.e. subdiag
|
||||
// a[i,j] is the multiplier used to eliminate the [i,j] term.
|
||||
// Solves the linear system J * p = -f (Jacobian * step = -residuals) for the Newton step.
|
||||
//
|
||||
// Replaces fjac_[0..nr_order_-1][*] by the LU decomposition.
|
||||
// index_[0..nr_order_-1] is an output vector of the row permutations.
|
||||
void
|
||||
DmpAlg::luDecomp()
|
||||
// This implementation uses a "Determinant Guarded" solver:
|
||||
// 1. Manually computes/checks the determinant of the Jacobian (safety guard).
|
||||
// 2. If the determinant is dangerously close to zero (< 1e-12), throws a DmpError.
|
||||
// 3. Otherwise, uses Eigen's highly optimized analytical inverse (fast path).
|
||||
//
|
||||
// Performance Note:
|
||||
// Analytical solvers are extremely fast for 2x2 and 3x3 matrices because they
|
||||
// contain no loops or branching, allowing the compiler to unroll them and use
|
||||
// SIMD instructions. This yields a ~23% speedup over LU decomposition in optimized builds.
|
||||
//
|
||||
// Numerical Stability Note:
|
||||
// If this analytical approach ever causes numerical issues (e.g., in extremely
|
||||
// ill-conditioned systems where the determinant is > 1e-12 but still causes loss
|
||||
// of precision), it can be TRIVIALLY swapped back to a robust LU decomposition
|
||||
// with partial pivoting by replacing the body of this function with:
|
||||
//
|
||||
// Eigen::Vector3d p = Eigen::Vector3d::Zero();
|
||||
// if (nr_order_ == 2) {
|
||||
// auto lu = fjac.topLeftCorner<2, 2>().partialPivLu();
|
||||
// if (std::abs(lu.matrixLU().diagonal().prod()) < 1e-12) {
|
||||
// throw DmpError("Jacobian is singular (order 2)");
|
||||
// }
|
||||
// p.head<2>() = lu.solve(-fvec.head<2>());
|
||||
// return p;
|
||||
// }
|
||||
// auto lu = fjac.partialPivLu();
|
||||
// if (std::abs(lu.matrixLU().diagonal().prod()) < 1e-12) {
|
||||
// throw DmpError("Jacobian is singular (order 3)");
|
||||
// }
|
||||
// p = lu.solve(-fvec);
|
||||
// return p;
|
||||
//
|
||||
Eigen::Vector3d
|
||||
DmpAlg::solveNewtonStep(const Eigen::Matrix3d &fjac,
|
||||
const Eigen::Vector3d &fvec)
|
||||
{
|
||||
const int size = nr_order_;
|
||||
Eigen::Vector3d p = Eigen::Vector3d::Zero();
|
||||
if (nr_order_ == 2) {
|
||||
double det = fjac.topLeftCorner<2, 2>().determinant();
|
||||
if (std::abs(det) < 1e-12) {
|
||||
throw DmpError("Jacobian is singular (order 2)");
|
||||
}
|
||||
p.head<2>() = fjac.topLeftCorner<2, 2>().inverse() * -fvec.head<2>();
|
||||
return p;
|
||||
}
|
||||
|
||||
// Find implicit scaling factors.
|
||||
for (int i = 0; i < size; i++) {
|
||||
double big = 0.0;
|
||||
for (int j = 0; j < size; j++) {
|
||||
double temp = std::abs(fjac_[i][j]);
|
||||
big = std::max(temp, big);
|
||||
}
|
||||
if (big == 0.0)
|
||||
throw DmpError("LU decomposition: no non-zero row element");
|
||||
scale_[i] = 1.0 / big;
|
||||
}
|
||||
int size_1 = size - 1;
|
||||
for (int j = 0; j < size; j++) {
|
||||
// Run down jth column from top to diag, to form the elements of U.
|
||||
for (int i = 0; i < j; i++) {
|
||||
double sum = fjac_[i][j];
|
||||
for (int k = 0; k < i; k++)
|
||||
sum -= fjac_[i][k] * fjac_[k][j];
|
||||
fjac_[i][j] = sum;
|
||||
}
|
||||
// Run down jth subdiag to form the residuals after the elimination
|
||||
// of the first j-1 subdiags. These residuals diviyded by the
|
||||
// appropriate diagonal term will become the multipliers in the
|
||||
// elimination of the jth. subdiag. Find index of largest scaled
|
||||
// term in imax.
|
||||
double big = 0.0;
|
||||
int imax = 0;
|
||||
for (int i = j; i < size; i++) {
|
||||
double sum = fjac_[i][j];
|
||||
for (int k = 0; k < j; k++)
|
||||
sum -= fjac_[i][k] * fjac_[k][j];
|
||||
fjac_[i][j] = sum;
|
||||
double dum = scale_[i] * std::abs(sum);
|
||||
if (dum >= big) {
|
||||
big = dum;
|
||||
imax = i;
|
||||
}
|
||||
}
|
||||
// Permute current row with imax.
|
||||
if (j != imax) {
|
||||
// Yes, do so...
|
||||
for (int k = 0; k < size; k++) {
|
||||
double dum = fjac_[imax][k];
|
||||
fjac_[imax][k] = fjac_[j][k];
|
||||
fjac_[j][k] = dum;
|
||||
}
|
||||
scale_[imax] = scale_[j];
|
||||
}
|
||||
index_[j] = imax;
|
||||
// If diag term is not zero divide subdiag to form multipliers.
|
||||
if (fjac_[j][j] == 0.0)
|
||||
fjac_[j][j] = tiny_double_;
|
||||
if (j != size_1) {
|
||||
double pivot = 1.0 / fjac_[j][j];
|
||||
for (int i = j + 1; i < size; i++)
|
||||
fjac_[i][j] *= pivot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Solves fjac_ * x = p_ for x, assuming fjac_ is LU form from luDecomp.
|
||||
// Solution overwrites p_.
|
||||
void
|
||||
DmpAlg::luSolve()
|
||||
{
|
||||
const int size = nr_order_;
|
||||
|
||||
// Transform p_ allowing for leading zeros.
|
||||
int non_zero = -1;
|
||||
for (int i = 0; i < size; i++) {
|
||||
int iperm = index_[i];
|
||||
double sum = p_[iperm];
|
||||
p_[iperm] = p_[i];
|
||||
if (non_zero != -1) {
|
||||
for (int j = non_zero; j <= i - 1; j++)
|
||||
sum -= fjac_[i][j] * p_[j];
|
||||
}
|
||||
else {
|
||||
if (sum != 0.0)
|
||||
non_zero = i;
|
||||
}
|
||||
p_[i] = sum;
|
||||
}
|
||||
// Backsubstitution.
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
double sum = p_[i];
|
||||
for (int j = i + 1; j < size; j++)
|
||||
sum -= fjac_[i][j] * p_[j];
|
||||
p_[i] = sum / fjac_[i][i];
|
||||
double det = fjac.determinant();
|
||||
if (std::abs(det) < 1e-12) {
|
||||
throw DmpError("Jacobian is singular (order 3)");
|
||||
}
|
||||
p = fjac.inverse() * -fvec;
|
||||
return p;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@
|
|||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include "LibertyClass.hh"
|
||||
#include "LumpedCapDelayCalc.hh"
|
||||
|
||||
|
|
@ -59,18 +62,21 @@ public:
|
|||
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;
|
||||
virtual void
|
||||
evalDmpEqns(Eigen::Vector3d &x,
|
||||
Eigen::Vector3d &fvec,
|
||||
Eigen::Matrix3d &fjac) = 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();
|
||||
void newtonRaphson(Eigen::Vector3d &x);
|
||||
// Solves J * p = -f using a fast analytical solver with singularity checks.
|
||||
// Can be easily swapped to LU with partial pivoting if needed.
|
||||
Eigen::Vector3d solveNewtonStep(const Eigen::Matrix3d &fjac,
|
||||
const Eigen::Vector3d &fvec);
|
||||
// Find driver parameters t0, delta_t, Ceff.
|
||||
void findDriverParams(double ceff);
|
||||
std::pair<double, double> gateCapDelaySlew(double ceff);
|
||||
|
|
@ -84,9 +90,9 @@ protected:
|
|||
double cl);
|
||||
double y0dcl(double t,
|
||||
double cl);
|
||||
void showX();
|
||||
void showFvec();
|
||||
void showJacobian();
|
||||
void showX(const Eigen::Vector3d &x);
|
||||
void showFvec(const Eigen::Vector3d &fvec);
|
||||
void showJacobian(const Eigen::Matrix3d &fjac);
|
||||
std::pair<double, double> findDriverDelaySlew();
|
||||
double findVoCrossing(double vth,
|
||||
double t_lower,
|
||||
|
|
@ -148,12 +154,7 @@ protected:
|
|||
|
||||
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_;
|
||||
|
|
@ -194,7 +195,10 @@ public:
|
|||
std::pair<double, double> gateDelaySlew() override;
|
||||
std::pair<double, double> loadDelaySlew(const Pin *,
|
||||
double elmore) override;
|
||||
void evalDmpEqns() override;
|
||||
void
|
||||
evalDmpEqns(Eigen::Vector3d &x,
|
||||
Eigen::Vector3d &fvec,
|
||||
Eigen::Matrix3d &fjac) override;
|
||||
|
||||
protected:
|
||||
double voCrossingUpperBound() override;
|
||||
|
|
@ -219,7 +223,10 @@ public:
|
|||
double rpi,
|
||||
double c1) override;
|
||||
std::pair<double, double> gateDelaySlew() override;
|
||||
void evalDmpEqns() override;
|
||||
void
|
||||
evalDmpEqns(Eigen::Vector3d &x,
|
||||
Eigen::Vector3d &fvec,
|
||||
Eigen::Matrix3d &fjac) override;
|
||||
|
||||
protected:
|
||||
double voCrossingUpperBound() override;
|
||||
|
|
@ -255,7 +262,10 @@ class DmpOnePole : public DmpAlg
|
|||
{
|
||||
public:
|
||||
DmpOnePole(StaState *sta);
|
||||
void evalDmpEqns() override;
|
||||
void
|
||||
evalDmpEqns(Eigen::Vector3d &x,
|
||||
Eigen::Vector3d &fvec,
|
||||
Eigen::Matrix3d &fjac) override;
|
||||
|
||||
protected:
|
||||
double voCrossingUpperBound() override;
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ GraphDelayCalc::delayInvalid(Vertex *vertex)
|
|||
{
|
||||
debugPrint(debug_, "delay_calc", 2, "delay invalid {}",
|
||||
vertex->to_string(this));
|
||||
if (graph_ && incremental_) {
|
||||
if (incremental_) {
|
||||
invalid_delays_.insert(vertex);
|
||||
// Invalidate driver that triggers dcalc for multi-driver nets.
|
||||
MultiDrvrNet *multi_drvr = multiDrvrNet(vertex);
|
||||
|
|
@ -338,47 +338,45 @@ FindVertexDelays::visit(Vertex *vertex)
|
|||
void
|
||||
GraphDelayCalc::findDelays(Level level)
|
||||
{
|
||||
if (arc_delay_calc_) {
|
||||
Stats stats(debug_, report_);
|
||||
int dcalc_count = 0;
|
||||
debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level);
|
||||
if (!delays_seeded_) {
|
||||
iter_->clear();
|
||||
seedRootSlews();
|
||||
delays_seeded_ = true;
|
||||
}
|
||||
else
|
||||
iter_->ensureSize();
|
||||
if (incremental_)
|
||||
seedInvalidDelays();
|
||||
|
||||
if (!iter_->empty()) {
|
||||
FindVertexDelays visitor(this);
|
||||
dcalc_count += iter_->visitParallel(level, &visitor);
|
||||
}
|
||||
|
||||
// Timing checks require slews at both ends of the arc,
|
||||
// so find their delays after all slews are known.
|
||||
for (Edge *check_edge : invalid_check_edges_)
|
||||
findCheckEdgeDelays(check_edge, arc_delay_calc_);
|
||||
invalid_check_edges_.clear();
|
||||
|
||||
for (Edge *latch_edge : invalid_latch_edges_)
|
||||
findLatchEdgeDelays(latch_edge);
|
||||
invalid_latch_edges_.clear();
|
||||
|
||||
delays_exist_ = true;
|
||||
incremental_ = true;
|
||||
debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count);
|
||||
stats.report("Delay calc");
|
||||
Stats stats(debug_, report_);
|
||||
int dcalc_count = 0;
|
||||
debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level);
|
||||
if (!delays_seeded_) {
|
||||
iter_->clear();
|
||||
seedRootSlews();
|
||||
delays_seeded_ = true;
|
||||
}
|
||||
else
|
||||
iter_->ensureSize();
|
||||
if (incremental_)
|
||||
seedInvalidDelays();
|
||||
|
||||
if (!iter_->empty()) {
|
||||
FindVertexDelays visitor(this);
|
||||
dcalc_count += iter_->visitParallel(level, &visitor);
|
||||
}
|
||||
|
||||
// Timing checks require slews at both ends of the arc,
|
||||
// so find their delays after all slews are known.
|
||||
for (Edge *check_edge : invalid_check_edges_)
|
||||
findCheckEdgeDelays(check_edge, arc_delay_calc_);
|
||||
invalid_check_edges_.clear();
|
||||
|
||||
for (Edge *latch_edge : invalid_latch_edges_)
|
||||
findLatchEdgeDelays(latch_edge);
|
||||
invalid_latch_edges_.clear();
|
||||
|
||||
delays_exist_ = true;
|
||||
incremental_ = true;
|
||||
debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count);
|
||||
stats.report("Delay calc");
|
||||
}
|
||||
|
||||
void
|
||||
GraphDelayCalc::seedInvalidDelays()
|
||||
{
|
||||
for (Vertex *vertex : invalid_delays_)
|
||||
iter_->enqueue(vertex);
|
||||
iter_->enqueue(vertex);
|
||||
invalid_delays_.clear();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@
|
|||
|
||||
This file summarizes STA API changes for each release.
|
||||
|
||||
2026/06/22
|
||||
----------
|
||||
|
||||
Liberty::hasSequentials has been renamed isSequential.
|
||||
|
||||
Release 3.1.0 2026/03/25
|
||||
------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ link_design top
|
|||
create_clock -period 50 clk
|
||||
set_input_delay -clock clk 1 {in1 in2}
|
||||
set sta_pocv_mode skew_normal
|
||||
report_checks -fields {slew variation input_pin variation} -digits 3
|
||||
report_checks -fields {slew variation input_pin} -digits 3
|
||||
|
||||
Startpoint: r2 (rising edge-triggered flip-flop clocked by clk)
|
||||
Endpoint: r3 (rising edge-triggered flip-flop clocked by clk)
|
||||
|
|
|
|||
118
graph/Graph.cc
118
graph/Graph.cc
|
|
@ -32,6 +32,7 @@
|
|||
#include "Mutex.hh"
|
||||
#include "Network.hh"
|
||||
#include "PortDirection.hh"
|
||||
#include "SearchPred.hh"
|
||||
#include "Stats.hh"
|
||||
#include "TimingArc.hh"
|
||||
#include "TimingRole.hh"
|
||||
|
|
@ -49,9 +50,9 @@ namespace sta {
|
|||
Graph::Graph(StaState *sta,
|
||||
DcalcAPIndex ap_count) :
|
||||
StaState(sta),
|
||||
ap_count_(ap_count),
|
||||
period_check_annotations_(network_),
|
||||
reg_clk_vertices_(makeVertexSet(this))
|
||||
reg_clk_vertices_(makeVertexSet(this)),
|
||||
ap_count_(ap_count)
|
||||
{
|
||||
// For the benifit of reg_clk_vertices_ that references graph_.
|
||||
graph_ = this;
|
||||
|
|
@ -487,7 +488,7 @@ Graph::deleteVertex(Vertex *vertex)
|
|||
EdgeId edge_id, next_id;
|
||||
for (edge_id = vertex->in_edges_; edge_id; edge_id = next_id) {
|
||||
Edge *edge = Graph::edge(edge_id);
|
||||
next_id = edge->vertex_in_link_;
|
||||
next_id = edge->vertex_in_next_;
|
||||
deleteOutEdge(edge->from(this), edge);
|
||||
edge->clear();
|
||||
edges_->destroy(edge);
|
||||
|
|
@ -508,7 +509,7 @@ bool
|
|||
Graph::hasFaninOne(Vertex *vertex) const
|
||||
{
|
||||
return vertex->in_edges_
|
||||
&& edge(vertex->in_edges_)->vertex_in_link_ == 0;
|
||||
&& edge(vertex->in_edges_)->vertex_in_next_ == 0;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -519,12 +520,12 @@ Graph::deleteInEdge(Vertex *vertex,
|
|||
EdgeId prev = 0;
|
||||
for (EdgeId i = vertex->in_edges_;
|
||||
i && i != edge_id;
|
||||
i = Graph::edge(i)->vertex_in_link_)
|
||||
i = Graph::edge(i)->vertex_in_next_)
|
||||
prev = i;
|
||||
if (prev)
|
||||
Graph::edge(prev)->vertex_in_link_ = edge->vertex_in_link_;
|
||||
Graph::edge(prev)->vertex_in_next_ = edge->vertex_in_next_;
|
||||
else
|
||||
vertex->in_edges_ = edge->vertex_in_link_;
|
||||
vertex->in_edges_ = edge->vertex_in_next_;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -574,6 +575,76 @@ Graph::gateEdgeArc(const Pin *in_pin,
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
Graph::visitFanouts(Vertex *vertex,
|
||||
SearchPred *pred,
|
||||
const VertexFn &fn)
|
||||
{
|
||||
if (pred->searchFrom(vertex)) {
|
||||
for (Edge *edge = this->edge(vertex->out_edges_);
|
||||
edge;
|
||||
edge = this->edge(edge->vertex_out_next_)) {
|
||||
Vertex *to_vertex = this->vertex(edge->to_);
|
||||
if (pred->searchThru(edge)
|
||||
&& pred->searchTo(to_vertex))
|
||||
fn(to_vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Graph::visitFanoutEdges(Vertex *vertex,
|
||||
SearchPred *pred,
|
||||
const EdgeFn &fn)
|
||||
{
|
||||
if (pred->searchFrom(vertex)) {
|
||||
for (Edge *edge = this->edge(vertex->out_edges_);
|
||||
edge;
|
||||
edge = this->edge(edge->vertex_out_next_)) {
|
||||
Vertex *to_vertex = this->vertex(edge->to_);
|
||||
if (pred->searchThru(edge)
|
||||
&& pred->searchTo(to_vertex))
|
||||
fn(edge, to_vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Graph::visitFanins(Vertex *vertex,
|
||||
SearchPred *pred,
|
||||
const VertexFn &fn)
|
||||
{
|
||||
if (pred->searchFrom(vertex)) {
|
||||
for (Edge *edge = this->edge(vertex->in_edges_);
|
||||
edge;
|
||||
edge = this->edge(edge->vertex_in_next_)) {
|
||||
Vertex *from_vertex = this->vertex(edge->from_);
|
||||
if (pred->searchThru(edge)
|
||||
&& pred->searchFrom(from_vertex))
|
||||
fn(from_vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Graph::visitFaninEdges(Vertex *vertex,
|
||||
SearchPred *pred,
|
||||
const EdgeFn &fn)
|
||||
{
|
||||
if (pred->searchFrom(vertex)) {
|
||||
for (Edge *edge = this->edge(vertex->in_edges_);
|
||||
edge;
|
||||
edge = this->edge(edge->vertex_in_next_)) {
|
||||
Vertex *from_vertex = this->vertex(edge->from_);
|
||||
if (pred->searchThru(edge)
|
||||
&& pred->searchFrom(from_vertex))
|
||||
fn(edge, from_vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
Slew
|
||||
Graph::slew(const Vertex *vertex,
|
||||
const RiseFall *rf,
|
||||
|
|
@ -650,7 +721,7 @@ Graph::makeEdge(Vertex *from,
|
|||
from->out_edges_ = edge_id;
|
||||
|
||||
// Add in edge to to vertex.
|
||||
edge->vertex_in_link_ = to->in_edges_;
|
||||
edge->vertex_in_next_ = to->in_edges_;
|
||||
to->in_edges_ = edge_id;
|
||||
|
||||
initArcDelays(edge);
|
||||
|
|
@ -969,22 +1040,22 @@ Vertex::init(Pin *pin,
|
|||
bool is_reg_clk)
|
||||
{
|
||||
pin_ = pin;
|
||||
is_reg_clk_ = is_reg_clk;
|
||||
is_bidirect_drvr_ = is_bidirect_drvr;
|
||||
in_edges_ = edge_id_null;
|
||||
out_edges_ = edge_id_null;
|
||||
slews_ = nullptr;
|
||||
paths_ = nullptr;
|
||||
tag_group_index_ = tag_group_index_max;
|
||||
slew_annotated_ = false;
|
||||
bfs_in_queue_ = 0;
|
||||
is_bidirect_drvr_ = is_bidirect_drvr;
|
||||
is_reg_clk_ = is_reg_clk;
|
||||
has_checks_ = false;
|
||||
is_check_clk_ = false;
|
||||
has_downstream_clk_pin_ = false;
|
||||
level_ = 0;
|
||||
visited1_ = false;
|
||||
visited2_ = false;
|
||||
has_sim_value_ = false;
|
||||
bfs_in_queue_ = 0;
|
||||
level_ = 0;
|
||||
slew_annotated_ = false;
|
||||
}
|
||||
|
||||
Vertex::~Vertex()
|
||||
|
|
@ -1210,20 +1281,19 @@ Edge::init(VertexId from,
|
|||
VertexId to,
|
||||
TimingArcSet *arc_set)
|
||||
{
|
||||
from_ = from;
|
||||
to_ = to;
|
||||
arc_set_ = arc_set;
|
||||
vertex_in_link_ = edge_id_null;
|
||||
vertex_out_next_ = edge_id_null;
|
||||
vertex_out_prev_ = edge_id_null;
|
||||
is_bidirect_inst_path_ = false;
|
||||
is_bidirect_net_path_ = false;
|
||||
is_bidirect_port_path_ = false;
|
||||
|
||||
arc_delays_ = nullptr;
|
||||
arc_delay_annotated_is_bits_ = true;
|
||||
arc_delay_annotated_.bits_ = 0;
|
||||
from_ = from;
|
||||
to_ = to;
|
||||
vertex_in_next_ = edge_id_null;
|
||||
vertex_out_next_ = edge_id_null;
|
||||
vertex_out_prev_ = edge_id_null;
|
||||
delay_annotation_is_incremental_ = false;
|
||||
is_bidirect_inst_path_ = false;
|
||||
is_bidirect_net_path_ = false;
|
||||
is_bidirect_port_path_ = false;
|
||||
is_disabled_loop_ = false;
|
||||
has_sim_sense_ = false;
|
||||
has_disabled_cond_ = false;
|
||||
|
|
@ -1464,6 +1534,8 @@ VertexIterator::findNext()
|
|||
findNextPin();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
VertexInEdgeIterator::VertexInEdgeIterator(Vertex *vertex,
|
||||
const Graph *graph) :
|
||||
next_(graph->edge(vertex->in_edges_)),
|
||||
|
|
@ -1483,7 +1555,7 @@ VertexInEdgeIterator::next()
|
|||
{
|
||||
Edge *next = next_;
|
||||
if (next_)
|
||||
next_ = graph_->edge(next_->vertex_in_link_);
|
||||
next_ = graph_->edge(next_->vertex_in_next_);
|
||||
return next;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,12 +81,12 @@ protected:
|
|||
void removeCell(ConcreteCell *cell);
|
||||
|
||||
std::string name_;
|
||||
ObjectId id_;
|
||||
std::string filename_;
|
||||
ConcreteCellMap cell_map_;
|
||||
ObjectId id_;
|
||||
bool is_liberty_;
|
||||
char bus_brkt_left_{'['};
|
||||
char bus_brkt_right_{']'};
|
||||
ConcreteCellMap cell_map_;
|
||||
|
||||
private:
|
||||
friend class ConcreteCell;
|
||||
|
|
|
|||
|
|
@ -366,10 +366,10 @@ protected:
|
|||
ConcretePort *port_;
|
||||
ConcreteNet *net_;
|
||||
ConcreteTerm *term_{nullptr};
|
||||
ObjectId id_;
|
||||
// Doubly linked list of net pins.
|
||||
ConcretePin *net_next_{nullptr};
|
||||
ConcretePin *net_prev_{nullptr};
|
||||
ObjectId id_;
|
||||
VertexId vertex_id_{vertex_id_null};
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -87,6 +87,19 @@ public:
|
|||
bool hasFaninOne(Vertex *vertex) const;
|
||||
VertexId vertexCount() { return vertices_->size(); }
|
||||
|
||||
void visitFanouts(Vertex *vertex,
|
||||
SearchPred *pred,
|
||||
const VertexFn &fn);
|
||||
void visitFanins(Vertex *vertex,
|
||||
SearchPred *pred,
|
||||
const VertexFn &fn);
|
||||
void visitFanoutEdges(Vertex *vertex,
|
||||
SearchPred *pred,
|
||||
const EdgeFn &fn);
|
||||
void visitFaninEdges(Vertex *vertex,
|
||||
SearchPred *pred,
|
||||
const EdgeFn &fn);
|
||||
|
||||
// Reported slew are the same as those in the liberty tables.
|
||||
// reported_slews = measured_slews / slew_derate_from_library
|
||||
// Measured slews are between slew_lower_threshold and slew_upper_threshold.
|
||||
|
|
@ -179,7 +192,7 @@ public:
|
|||
VertexSet ®ClkVertices() { return reg_clk_vertices_; }
|
||||
|
||||
static constexpr int vertex_level_bits = 24;
|
||||
static constexpr int vertex_level_max = (1<<vertex_level_bits)-1;
|
||||
static constexpr int vertex_level_max = (1<<vertex_level_bits) - 1;
|
||||
|
||||
protected:
|
||||
void makeVerticesAndEdges();
|
||||
|
|
@ -216,11 +229,11 @@ protected:
|
|||
// driver/source (top level input, instance pin output) vertex
|
||||
// in pin_bidirect_drvr_vertex_map
|
||||
PinVertexMap pin_bidirect_drvr_vertex_map_;
|
||||
DcalcAPIndex ap_count_;
|
||||
// Sdf period check annotations.
|
||||
PeriodCheckAnnotations period_check_annotations_;
|
||||
// Register/latch clock vertices to search from.
|
||||
VertexSet reg_clk_vertices_;
|
||||
DcalcAPIndex ap_count_;
|
||||
|
||||
friend class Vertex;
|
||||
friend class VertexIterator;
|
||||
|
|
@ -311,21 +324,20 @@ protected:
|
|||
// Each bit corresponds to a different BFS queue.
|
||||
std::atomic<uint8_t> bfs_in_queue_; // 8
|
||||
|
||||
int level_:Graph::vertex_level_bits; // 24
|
||||
unsigned int slew_annotated_:slew_annotated_bits; // 4
|
||||
// Bidirect pins have two vertices.
|
||||
// This flag distinguishes the driver and load vertices.
|
||||
bool is_bidirect_drvr_:1;
|
||||
|
||||
bool is_reg_clk_:1;
|
||||
unsigned int is_bidirect_drvr_:1;
|
||||
unsigned int is_reg_clk_:1;
|
||||
// Constrained by timing check edge.
|
||||
bool has_checks_:1;
|
||||
unsigned int has_checks_:1;
|
||||
// Is the clock for a timing check.
|
||||
bool is_check_clk_:1;
|
||||
bool has_downstream_clk_pin_:1;
|
||||
bool visited1_:1;
|
||||
bool visited2_:1;
|
||||
bool has_sim_value_:1;
|
||||
unsigned int is_check_clk_:1;
|
||||
unsigned int has_downstream_clk_pin_:1;
|
||||
unsigned int visited1_:1;
|
||||
unsigned int visited2_:1;
|
||||
unsigned int has_sim_value_:1;
|
||||
int level_:Graph::vertex_level_bits; // 24
|
||||
unsigned int slew_annotated_:slew_annotated_bits; // 4
|
||||
|
||||
private:
|
||||
friend class Graph;
|
||||
|
|
@ -392,16 +404,16 @@ protected:
|
|||
static uintptr_t arcDelayAnnotateBit(size_t index);
|
||||
|
||||
TimingArcSet *arc_set_;
|
||||
VertexId from_;
|
||||
VertexId to_;
|
||||
EdgeId vertex_in_link_; // Vertex in edges list.
|
||||
EdgeId vertex_out_next_; // Vertex out edges doubly linked list.
|
||||
EdgeId vertex_out_prev_;
|
||||
float *arc_delays_;
|
||||
union {
|
||||
uintptr_t bits_;
|
||||
std::vector<bool> *seq_;
|
||||
} arc_delay_annotated_;
|
||||
VertexId from_;
|
||||
VertexId to_;
|
||||
EdgeId vertex_in_next_; // Vertex in edges list.
|
||||
EdgeId vertex_out_next_; // Vertex out edges doubly linked list.
|
||||
EdgeId vertex_out_prev_;
|
||||
bool arc_delay_annotated_is_bits_:1;
|
||||
bool delay_annotation_is_incremental_:1;
|
||||
bool is_bidirect_inst_path_:1;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ using Level = int;
|
|||
using DcalcAPIndex = int;
|
||||
using TagGroupIndex = int;
|
||||
using SlewSeq = std::vector<Slew>;
|
||||
using VertexFn = std::function<void(Vertex*)>;
|
||||
using EdgeFn = std::function<void(Edge *, Vertex*)>;
|
||||
|
||||
static constexpr int level_max = std::numeric_limits<Level>::max();
|
||||
|
||||
|
|
|
|||
|
|
@ -536,7 +536,9 @@ public:
|
|||
bool leakagePowerExists() const { return leakage_power_exists_; }
|
||||
|
||||
// Register, Latch or Statetable.
|
||||
bool hasSequentials() const;
|
||||
bool isSequential() const;
|
||||
// deprecated 2026-06-22 (use isSequential)
|
||||
bool hasSequentials() const __attribute__ ((deprecated));
|
||||
const SequentialSeq &sequentials() const { return sequentials_; }
|
||||
// Find the sequential with the output connected to an (internal) port.
|
||||
Sequential *outputPortSequential(LibertyPort *port);
|
||||
|
|
@ -861,6 +863,8 @@ public:
|
|||
void setIsRegOutput(bool is_reg_out);
|
||||
bool isLatchData() const { return is_latch_data_; }
|
||||
void setIsLatchData(bool is_latch_data);
|
||||
bool isLatchOutput() const { return is_latch_output_; }
|
||||
void setIsLatchOutput(bool is_latch_output);
|
||||
// Is the clock for timing checks.
|
||||
bool isCheckClk() const { return is_check_clk_; }
|
||||
void setIsCheckClk(bool is_clk);
|
||||
|
|
@ -956,6 +960,7 @@ protected:
|
|||
bool is_reg_clk_:1 {false};
|
||||
bool is_reg_output_:1 {false};
|
||||
bool is_latch_data_: 1 {false};
|
||||
bool is_latch_output_:1 {false};
|
||||
bool is_check_clk_:1 {false};
|
||||
bool is_clk_gate_clk_:1 {false};
|
||||
bool is_clk_gate_enable_:1 {false};
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ public:
|
|||
// Pin clocks a timing check.
|
||||
[[nodiscard]] bool isCheckClk(const Pin *pin) const;
|
||||
[[nodiscard]] bool isLatchData(const Pin *pin) const;
|
||||
[[nodiscard]] bool isLatchOutput(const Pin *pin) const;
|
||||
|
||||
// Iterate over all of the pins connected to a pin and the parent
|
||||
// and child nets it is hierarchically connected to (port, leaf and
|
||||
|
|
|
|||
|
|
@ -577,6 +577,7 @@ public:
|
|||
const RiseFall *clk_rf,
|
||||
const MinMaxAll *min_max);
|
||||
void ensureInputDelayRefPinEdges();
|
||||
void inputDelayRefPinEdgesInvalid();
|
||||
void setOutputDelay(const Pin *pin,
|
||||
const RiseFallBoth *rf,
|
||||
const Clock *clk,
|
||||
|
|
|
|||
|
|
@ -284,8 +284,8 @@ public:
|
|||
Vertex *vertex,
|
||||
const Mode *mode,
|
||||
TagGroupBldr *tag_bldr);
|
||||
void enqueueLatchDataOutputs(Vertex *vertex);
|
||||
void enqueueLatchOutput(Vertex *vertex);
|
||||
void postponeLatchDataOutputs(Vertex *vertex);
|
||||
void postponeArrivals(Vertex *vertex);
|
||||
void enqueuePendingClkFanouts();
|
||||
void postponeClkFanouts(Vertex *vertex);
|
||||
void seedRequired(Vertex *vertex);
|
||||
|
|
@ -406,7 +406,6 @@ public:
|
|||
void checkPrevPaths() const;
|
||||
void deletePaths(Vertex *vertex);
|
||||
void deleteTagGroup(TagGroup *group);
|
||||
bool postponeLatchOutputs() const { return postpone_latch_outputs_; }
|
||||
void saveEnumPath(Path *path);
|
||||
bool isSrchRoot(Vertex *vertex,
|
||||
const Mode *mode) const;
|
||||
|
|
@ -530,9 +529,6 @@ protected:
|
|||
const MinMax *min_max) const;
|
||||
void seedRequireds();
|
||||
void seedInvalidRequireds();
|
||||
[[nodiscard]] bool havePendingLatchOutputs();
|
||||
void clearPendingLatchOutputs();
|
||||
void enqueuePendingLatchOutputs();
|
||||
void findFilteredArrivals(bool thru_latches);
|
||||
void findArrivalsSeed();
|
||||
void seedFilterStarts();
|
||||
|
|
@ -594,7 +590,7 @@ protected:
|
|||
|
||||
// Search predicates.
|
||||
SearchPred *search_thru_;
|
||||
SearchAdj *search_adj_;
|
||||
SearchPred *search_adj_;
|
||||
EvalPred *eval_pred_;
|
||||
|
||||
// Some arrivals exist.
|
||||
|
|
@ -651,11 +647,11 @@ protected:
|
|||
std::mutex tag_group_lock_;
|
||||
|
||||
// Latches data outputs to queue on the next search pass.
|
||||
VertexSet pending_latch_outputs_;
|
||||
std::mutex pending_latch_outputs_lock_;
|
||||
VertexSet postponed_arrivals_;
|
||||
std::mutex postponed_arrivals_lock_;
|
||||
// Clock network endpoints where arrival search was suppended by findClkArrivals().
|
||||
VertexSet pending_clk_endpoints_;
|
||||
std::mutex pending_clk_endpoints_lock_;
|
||||
VertexSet postponed_clk_endpoints_;
|
||||
std::mutex postponed_clk_endpoints_lock_;
|
||||
|
||||
VertexSet endpoints_;
|
||||
bool endpoints_initialized_{false};
|
||||
|
|
@ -669,7 +665,6 @@ protected:
|
|||
std::mutex filtered_arrivals_lock_;
|
||||
|
||||
bool found_downstream_clk_pins_{false};
|
||||
bool postpone_latch_outputs_{false};
|
||||
std::vector<Path*> enum_paths_;
|
||||
|
||||
VisitPathEnds *visit_path_ends_;
|
||||
|
|
@ -712,7 +707,8 @@ public:
|
|||
bool make_tag_cache,
|
||||
const StaState *sta);
|
||||
~PathVisitor() override;
|
||||
virtual void visitFaninPaths(Vertex *to_vertex);
|
||||
virtual void visitFaninPaths(Vertex *to_vertex,
|
||||
bool with_latch_edges);
|
||||
virtual void visitFanoutPaths(Vertex *from_vertex);
|
||||
// Return false to stop visiting.
|
||||
virtual bool visitFromToPath(const Pin *from_pin,
|
||||
|
|
@ -779,6 +775,8 @@ public:
|
|||
SearchPred *pred);
|
||||
void copyState(const StaState *sta) override;
|
||||
void visit(Vertex *vertex) override;
|
||||
void visit(Vertex *vertex,
|
||||
bool with_latch_edges);
|
||||
VertexVisitor *copy() const override;
|
||||
// Return false to stop visiting.
|
||||
bool visitFromToPath(const Pin *from_pin,
|
||||
|
|
@ -804,12 +802,14 @@ protected:
|
|||
void pruneCrprArrivals();
|
||||
void constrainedRequiredsInvalid(Vertex *vertex,
|
||||
bool is_clk);
|
||||
bool hasPendingLoopPaths(Edge *edge) const;
|
||||
|
||||
bool always_to_endpoints_;
|
||||
bool always_save_prev_paths_;
|
||||
bool clks_only_;
|
||||
TagGroupBldr *tag_bldr_;
|
||||
TagGroupBldr *tag_bldr_no_crpr_;
|
||||
SearchPred *adj_pred_;
|
||||
SearchPred *search_adj_;
|
||||
bool crpr_active_;
|
||||
bool has_fanin_one_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,19 +51,19 @@ public:
|
|||
// Search is allowed from from_vertex.
|
||||
virtual bool searchFrom(const Vertex *from_vertex,
|
||||
const Mode *mode) const = 0;
|
||||
bool searchFrom(const Vertex *from_vertex) const;
|
||||
virtual bool searchFrom(const Vertex *from_vertex) const;
|
||||
// Search is allowed through edge.
|
||||
// from/to pins are NOT checked.
|
||||
// inst can be either the from_pin or to_pin instance because it
|
||||
// is only referenced when they are the same (non-wire edge).
|
||||
virtual bool searchThru(Edge *edge,
|
||||
const Mode *mode) const = 0;
|
||||
bool searchThru(Edge *edge) const;
|
||||
virtual bool searchThru(Edge *edge) const;
|
||||
// Search is allowed to to_pin.
|
||||
virtual bool searchTo(const Vertex *to_vertex,
|
||||
const Mode *mode) const = 0;
|
||||
bool searchTo(const Vertex *to_vertex) const;
|
||||
void copyState(const StaState *sta);
|
||||
virtual bool searchTo(const Vertex *to_vertex) const;
|
||||
virtual void copyState(const StaState *sta);
|
||||
|
||||
protected:
|
||||
const StaState *sta_;
|
||||
|
|
|
|||
|
|
@ -1518,7 +1518,6 @@ protected:
|
|||
bool infer_latches);
|
||||
void delayCalcPreamble();
|
||||
void delaysInvalidFrom(const Port *port);
|
||||
void delaysInvalidFromFanin(const Port *port);
|
||||
void deleteEdge(Edge *edge);
|
||||
void netParasiticCaps(Net *net,
|
||||
const RiseFall *rf,
|
||||
|
|
|
|||
|
|
@ -1438,6 +1438,12 @@ LibertyCell::outputPortSequential(LibertyPort *port)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
LibertyCell::isSequential() const
|
||||
{
|
||||
return !sequentials_.empty() || statetable_ != nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
LibertyCell::hasSequentials() const
|
||||
{
|
||||
|
|
@ -1618,7 +1624,7 @@ void
|
|||
LibertyCell::makeLatchEnables(Report *report,
|
||||
Debug *debug)
|
||||
{
|
||||
if (hasSequentials()
|
||||
if (isSequential()
|
||||
|| hasInferedRegTimingArcs()) {
|
||||
for (TimingArcSet *d_to_q : timing_arc_sets_) {
|
||||
if (d_to_q->role() == TimingRole::latchDtoQ()) {
|
||||
|
|
@ -1769,6 +1775,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d,
|
|||
latch_d_to_q_map_[d_to_q] = idx;
|
||||
latch_check_map_[setup_check] = idx;
|
||||
d->setIsLatchData(true);
|
||||
q->setIsLatchOutput(true);
|
||||
debugPrint(debug, "liberty_latch", 1,
|
||||
"latch {} -> {} | {} {} -> {} | {} {} -> {} setup",
|
||||
d->name(),
|
||||
|
|
@ -2434,6 +2441,13 @@ LibertyPort::setIsLatchData(bool is_latch_data)
|
|||
setMemberFlag(is_latch_data, &LibertyPort::setIsLatchData);
|
||||
}
|
||||
|
||||
void
|
||||
LibertyPort::setIsLatchOutput(bool is_latch_output)
|
||||
{
|
||||
is_latch_output_ = is_latch_output;
|
||||
setMemberFlag(is_latch_output, &LibertyPort::setIsLatchOutput);
|
||||
}
|
||||
|
||||
void
|
||||
LibertyPort::setIsCheckClk(bool is_clk)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ LibertyWriter::writeBusDcls()
|
|||
sta::print(stream_, " type (\"{}\") {{\n", dcl->name());
|
||||
sta::print(stream_, " base_type : array;\n");
|
||||
sta::print(stream_, " data_type : bit;\n");
|
||||
sta::print(stream_, " bit_width : {};\n", std::abs(dcl->from() - dcl->to() + 1));
|
||||
sta::print(stream_, " bit_width : {};\n", std::abs(dcl->from() - dcl->to()) + 1);
|
||||
sta::print(stream_, " bit_from : {};\n", dcl->from());
|
||||
sta::print(stream_, " bit_to : {};\n", dcl->to());
|
||||
sta::print(stream_, " }}\n");
|
||||
|
|
|
|||
|
|
@ -536,6 +536,9 @@ TimingArcSet::destroy()
|
|||
delete wire_timing_arc_set_;
|
||||
wire_timing_arc_set_ = nullptr;
|
||||
wire_timing_arc_attrs_ = nullptr;
|
||||
|
||||
delete port_refpin_timing_arc_set_;
|
||||
port_refpin_timing_arc_set_ = nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ ConcreteLibrary::ConcreteLibrary(std::string_view name,
|
|||
std::string_view filename,
|
||||
bool is_liberty) :
|
||||
name_(name),
|
||||
id_(ConcreteNetwork::nextObjectId()),
|
||||
filename_(filename),
|
||||
id_(ConcreteNetwork::nextObjectId()),
|
||||
is_liberty_(is_liberty)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -636,6 +636,16 @@ Network::isLatchData(const Pin *pin) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Network::isLatchOutput(const Pin *pin) const
|
||||
{
|
||||
LibertyPort *port = libertyPort(pin);
|
||||
if (port)
|
||||
return port->isLatchOutput();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ Power::power(const Scene *scene,
|
|||
pad.incr(inst_power);
|
||||
else if (inClockNetwork(inst, clk_network))
|
||||
clock.incr(inst_power);
|
||||
else if (cell->hasSequentials())
|
||||
else if (cell->isSequential())
|
||||
sequential.incr(inst_power);
|
||||
else
|
||||
combinational.incr(inst_power);
|
||||
|
|
@ -554,9 +554,12 @@ ActivitySrchPred::searchThru(Edge *edge,
|
|||
{
|
||||
const Sdc *sdc = mode->sdc();
|
||||
const TimingRole *role = edge->role();
|
||||
return !(edge->role()->isTimingCheck() || sdc->isDisabledConstraint(edge)
|
||||
|| sdc->isDisabledCondDefault(edge) || edge->isBidirectInstPath()
|
||||
|| edge->isDisabledLoop() || role == TimingRole::regClkToQ()
|
||||
return !(edge->role()->isTimingCheck()
|
||||
|| sdc->isDisabledConstraint(edge)
|
||||
|| sdc->isDisabledCondDefault(edge)
|
||||
|| edge->isBidirectInstPath()
|
||||
|| edge->isDisabledLoop()
|
||||
|| role == TimingRole::regClkToQ()
|
||||
|| role->isLatchDtoQ());
|
||||
}
|
||||
|
||||
|
|
@ -692,7 +695,8 @@ PropActivityVisitor::visit(Vertex *vertex)
|
|||
if (cell) {
|
||||
LibertyCell *test_cell = cell->libertyCell()->testCell();
|
||||
if (network_->isLoad(pin)) {
|
||||
if (cell->hasSequentials() || (test_cell && test_cell->hasSequentials())) {
|
||||
if (cell->isSequential()
|
||||
|| (test_cell && test_cell->isSequential())) {
|
||||
debugPrint(debug_, "power_activity", 3, "pending seq {}",
|
||||
network_->pathName(inst));
|
||||
visited_regs_.insert(inst);
|
||||
|
|
@ -701,10 +705,8 @@ PropActivityVisitor::visit(Vertex *vertex)
|
|||
if (cell->isClockGate()) {
|
||||
const Pin *enable, *clk, *gclk;
|
||||
power_->clockGatePins(inst, enable, clk, gclk);
|
||||
if (gclk) {
|
||||
Vertex *gclk_vertex = graph_->pinDrvrVertex(gclk);
|
||||
bfs_->enqueue(gclk_vertex);
|
||||
}
|
||||
if (gclk)
|
||||
visited_regs_.insert(inst);
|
||||
}
|
||||
}
|
||||
bfs_->enqueueAdjacentVertices(vertex, mode_);
|
||||
|
|
@ -746,9 +748,9 @@ PropActivityVisitor::setActivityCheck(const Pin *pin,
|
|||
max_change_ = duty_delta;
|
||||
max_change_pin_ = pin;
|
||||
}
|
||||
bool changed = density_delta > change_tolerance_ || duty_delta > change_tolerance_
|
||||
|| activity.origin() != prev_activity.origin();
|
||||
;
|
||||
bool changed = density_delta > change_tolerance_
|
||||
|| duty_delta > change_tolerance_
|
||||
|| activity.origin() != prev_activity.origin();
|
||||
power_->setActivity(pin, activity);
|
||||
return changed;
|
||||
}
|
||||
|
|
@ -905,8 +907,9 @@ Power::ensureActivities(const Scene *scene)
|
|||
// unless it has been set by command.
|
||||
if (input_activity_.origin() == PwrActivityOrigin::unknown) {
|
||||
float min_period = clockMinPeriod(scene_->mode()->sdc());
|
||||
float density =
|
||||
0.1 / (min_period != 0.0 ? min_period : units_->timeUnit()->scale());
|
||||
if (min_period == 0.0)
|
||||
min_period = units_->timeUnit()->scale();
|
||||
float density = 0.1 / min_period;
|
||||
input_activity_.set(density, 0.5, PwrActivityOrigin::input);
|
||||
}
|
||||
ActivitySrchPred activity_srch_pred(this);
|
||||
|
|
@ -918,7 +921,7 @@ Power::ensureActivities(const Scene *scene)
|
|||
// Propagate activiities through registers.
|
||||
InstanceSet regs = std::move(visitor.visitedRegs());
|
||||
int pass = 1;
|
||||
while (!regs.empty() && pass < max_activity_passes_) {
|
||||
while (!regs.empty() && pass <= max_activity_passes_) {
|
||||
visitor.init();
|
||||
for (const Instance *reg : regs)
|
||||
// Propagate activiities across register D->Q.
|
||||
|
|
@ -927,8 +930,10 @@ Power::ensureActivities(const Scene *scene)
|
|||
// combinational logic.
|
||||
bfs.visit(levelize_->maxLevel(), &visitor);
|
||||
regs = std::move(visitor.visitedRegs());
|
||||
debugPrint(debug_, "power_activity", 1, "Pass {} change {:.2f} {}", pass,
|
||||
visitor.maxChange(), network_->pathName(visitor.maxChangePin()));
|
||||
debugPrint(debug_, "power_activity", 1, "Pass {} change {:.2f} {}",
|
||||
pass,
|
||||
visitor.maxChange(),
|
||||
network_->pathName(visitor.maxChangePin()));
|
||||
pass++;
|
||||
}
|
||||
}
|
||||
|
|
@ -972,6 +977,8 @@ Power::seedRegOutputActivities(const Instance *inst,
|
|||
const SequentialSeq &seqs = test_cell->sequentials();
|
||||
seedRegOutputActivities(inst, test_cell, seqs, bfs);
|
||||
}
|
||||
else if (cell->isClockGate())
|
||||
seedClkGateOutputActivities(inst, bfs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1045,6 +1052,23 @@ Power::seedRegOutputActivities(const Instance *reg,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Power::seedClkGateOutputActivities(const Instance *inst,
|
||||
BfsFwdIterator &bfs)
|
||||
{
|
||||
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
||||
while (pin_iter->hasNext()) {
|
||||
Pin *pin = pin_iter->next();
|
||||
LibertyPort *port = network_->libertyPort(pin);
|
||||
if (port && port->isClockGateOut()) {
|
||||
Vertex *vertex = graph_->pinDrvrVertex(pin);
|
||||
if (vertex)
|
||||
bfs.enqueue(vertex);
|
||||
}
|
||||
}
|
||||
delete pin_iter;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
|
|
@ -1566,8 +1590,12 @@ Power::findActivity(const Pin *pin)
|
|||
if (activity && activity->origin() != PwrActivityOrigin::unknown)
|
||||
return *activity;
|
||||
const Clock *clk = findClk(pin);
|
||||
float duty = clockDuty(clk);
|
||||
return PwrActivity(2.0 / clk->period(), duty, PwrActivityOrigin::clock);
|
||||
if (clk) {
|
||||
float duty = clockDuty(clk);
|
||||
return PwrActivity(2.0 / clk->period(), duty, PwrActivityOrigin::clock);
|
||||
}
|
||||
else
|
||||
return PwrActivity();
|
||||
}
|
||||
else if (global_activity_.isSet())
|
||||
return global_activity_;
|
||||
|
|
|
|||
|
|
@ -224,6 +224,8 @@ protected:
|
|||
const LibertyCell *test_cell,
|
||||
const SequentialSeq &seqs,
|
||||
BfsFwdIterator &bfs);
|
||||
void seedClkGateOutputActivities(const Instance *inst,
|
||||
BfsFwdIterator &bfs);
|
||||
PwrActivity evalActivity(FuncExpr *expr,
|
||||
const Instance *inst);
|
||||
PwrActivity evalActivity(FuncExpr *expr,
|
||||
|
|
|
|||
12
sdc/Sdc.cc
12
sdc/Sdc.cc
|
|
@ -2840,6 +2840,18 @@ Sdc::ensureInputDelayRefPinEdges()
|
|||
}
|
||||
}
|
||||
|
||||
// The ref_pin edges are owned by the graph. When the graph is deleted the
|
||||
// edges go with it, so the existence flags must be reset to force them to be
|
||||
// rebuilt by ensureInputDelayRefPinEdges() with the new graph.
|
||||
void
|
||||
Sdc::inputDelayRefPinEdgesInvalid()
|
||||
{
|
||||
if (have_input_delay_ref_pins_) {
|
||||
for (InputDelay *input_delay : input_delays_)
|
||||
input_delay->setRefPinEdgesExist(false);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -344,11 +344,7 @@ BfsIterator::remove(Vertex *vertex)
|
|||
BfsFwdIterator::BfsFwdIterator(BfsIndex bfs_index,
|
||||
SearchPred *search_pred,
|
||||
StaState *sta) :
|
||||
BfsIterator(bfs_index,
|
||||
0,
|
||||
level_max,
|
||||
search_pred,
|
||||
sta)
|
||||
BfsIterator(bfs_index, 0, Graph::vertex_level_max, search_pred, sta)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -416,11 +412,7 @@ BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex,
|
|||
BfsBkwdIterator::BfsBkwdIterator(BfsIndex bfs_index,
|
||||
SearchPred *search_pred,
|
||||
StaState *sta) :
|
||||
BfsIterator(bfs_index,
|
||||
level_max,
|
||||
0,
|
||||
search_pred,
|
||||
sta)
|
||||
BfsIterator(bfs_index, Graph::vertex_level_max, 0, search_pred, sta)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
#include <cmath>
|
||||
|
||||
#include "Bfs.hh"
|
||||
#include "Clock.hh"
|
||||
#include "ContainerHelpers.hh"
|
||||
#include "Debug.hh"
|
||||
|
|
@ -270,30 +269,38 @@ Genclks::ensureMaster(Clock *gclk,
|
|||
}
|
||||
if (!found_master) {
|
||||
// Search backward from generated clock source pin to a clock pin.
|
||||
GenClkMasterSearchPred pred(this);
|
||||
BfsBkwdIterator iter(BfsIndex::other, &pred, this);
|
||||
seedSrcPins(gclk, iter);
|
||||
while (iter.hasNext()) {
|
||||
Vertex *vertex = iter.next();
|
||||
Pin *pin = vertex->pin();
|
||||
if (sdc->isLeafPinClock(pin)) {
|
||||
ClockSet *master_clks = sdc->findLeafPinClocks(pin);
|
||||
if (master_clks) {
|
||||
ClockSet::iterator master_iter = master_clks->begin();
|
||||
if (master_iter != master_clks->end()) {
|
||||
master_clk = *master_iter++;
|
||||
// Master source pin can actually be a clock source pin.
|
||||
if (master_clk != gclk) {
|
||||
gclk->setInferedMasterClk(master_clk);
|
||||
debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(),
|
||||
master_clk->name());
|
||||
master_clk_count++;
|
||||
break;
|
||||
GenClkMasterSearchPred srch_pred(this);
|
||||
VertexQueue master_queue;
|
||||
VertexSet visited = makeVertexSet(this);
|
||||
VertexSet src_vertices = makeVertexSet(this);
|
||||
gclk->srcPinVertices(src_vertices, network_, graph_);
|
||||
for (Vertex *vertex : src_vertices)
|
||||
master_queue.push(vertex);
|
||||
while (!master_queue.empty()) {
|
||||
Vertex *vertex = master_queue.front();
|
||||
master_queue.pop();
|
||||
if (!visited.contains(vertex)) {
|
||||
visited.insert(vertex);
|
||||
Pin *pin = vertex->pin();
|
||||
if (sdc->isLeafPinClock(pin)) {
|
||||
ClockSet *master_clks = sdc->findLeafPinClocks(pin);
|
||||
if (master_clks) {
|
||||
ClockSet::iterator master_iter = master_clks->begin();
|
||||
if (master_iter != master_clks->end()) {
|
||||
master_clk = *master_iter++;
|
||||
// Master source pin can actually be a clock source pin.
|
||||
if (master_clk != gclk) {
|
||||
gclk->setInferedMasterClk(master_clk);
|
||||
debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(),
|
||||
master_clk->name());
|
||||
master_clk_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
enqueueFanin(vertex, master_queue, srch_pred);
|
||||
}
|
||||
iter.enqueueAdjacentVertices(vertex, mode_);
|
||||
}
|
||||
}
|
||||
if (master_clk_count > 1)
|
||||
|
|
@ -303,16 +310,6 @@ Genclks::ensureMaster(Clock *gclk,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Genclks::seedSrcPins(Clock *clk,
|
||||
BfsBkwdIterator &iter)
|
||||
{
|
||||
VertexSet src_vertices = makeVertexSet(this);
|
||||
clk->srcPinVertices(src_vertices, network_, graph_);
|
||||
for (Vertex *vertex : src_vertices)
|
||||
iter.enqueue(vertex);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// Similar to ClkTreeSearchPred but
|
||||
|
|
@ -349,32 +346,34 @@ Genclks::findFanin(Clock *gclk,
|
|||
{
|
||||
// Search backward from generated clock source pin to a clock pin.
|
||||
GenClkFaninSrchPred srch_pred(gclk, this);
|
||||
BfsBkwdIterator iter(BfsIndex::other, &srch_pred, this);
|
||||
seedClkVertices(gclk, iter, fanins);
|
||||
while (iter.hasNext()) {
|
||||
Vertex *vertex = iter.next();
|
||||
VertexQueue fanin_queue;
|
||||
seedClkVertices(gclk, fanin_queue, fanins, srch_pred);
|
||||
while (!fanin_queue.empty()) {
|
||||
Vertex *vertex = fanin_queue.front();
|
||||
fanin_queue.pop();
|
||||
if (!fanins.contains(vertex)) {
|
||||
fanins.insert(vertex);
|
||||
debugPrint(debug_, "genclk", 2, "gen clk {} fanin {}", gclk->name(),
|
||||
vertex->to_string(this));
|
||||
iter.enqueueAdjacentVertices(vertex, mode_);
|
||||
enqueueFanin(vertex, fanin_queue, srch_pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Genclks::seedClkVertices(Clock *clk,
|
||||
BfsBkwdIterator &iter,
|
||||
VertexSet &fanins)
|
||||
VertexQueue &fanin_queue,
|
||||
VertexSet &fanins,
|
||||
SearchPred &srch_pred)
|
||||
{
|
||||
for (const Pin *pin : clk->leafPins()) {
|
||||
Vertex *vertex, *bidirect_drvr_vertex;
|
||||
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
||||
fanins.insert(vertex);
|
||||
iter.enqueueAdjacentVertices(vertex, mode_);
|
||||
enqueueFanin(vertex, fanin_queue, srch_pred);
|
||||
if (bidirect_drvr_vertex) {
|
||||
fanins.insert(bidirect_drvr_vertex);
|
||||
iter.enqueueAdjacentVertices(bidirect_drvr_vertex, mode_);
|
||||
enqueueFanin(bidirect_drvr_vertex, fanin_queue, srch_pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -457,11 +456,11 @@ Genclks::findInsertionDelays(Clock *gclk)
|
|||
debugPrint(debug_, "genclk", 2, "find gen clk {} insertion", gclk->name());
|
||||
GenclkInfo *genclk_info = makeGenclkInfo(gclk);
|
||||
FilterPath *src_filter = genclk_info->srcFilter();
|
||||
VertexQueue insert_queue;
|
||||
GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this);
|
||||
BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this);
|
||||
seedSrcPins(gclk, src_filter, insert_iter);
|
||||
seedSrcPins(gclk, src_filter, insert_queue, srch_pred);
|
||||
// Propagate arrivals to generated clk root pin level.
|
||||
findSrcArrivals(gclk, insert_iter, genclk_info);
|
||||
findSrcArrivals(gclk, genclk_info, insert_queue);
|
||||
// Unregister the filter so that it is not triggered by other searches.
|
||||
// The exception itself has to stick around because the source path
|
||||
// tags reference it.
|
||||
|
|
@ -591,7 +590,8 @@ Genclks::makeSrcFilter(Clock *gclk,
|
|||
void
|
||||
Genclks::seedSrcPins(Clock *gclk,
|
||||
FilterPath *src_filter,
|
||||
BfsFwdIterator &insert_iter)
|
||||
VertexQueue &insert_queue,
|
||||
SearchPred &srch_pred)
|
||||
{
|
||||
Clock *master_clk = gclk->masterClk();
|
||||
for (const Pin *master_pin : master_clk->leafPins()) {
|
||||
|
|
@ -613,13 +613,35 @@ Genclks::seedSrcPins(Clock *gclk,
|
|||
tag_bldr.setArrival(tag, insert);
|
||||
}
|
||||
}
|
||||
search_->setVertexArrivals(vertex, &tag_bldr);
|
||||
insert_iter.enqueueAdjacentVertices(vertex, mode_);
|
||||
}
|
||||
search_->setVertexArrivals(vertex, &tag_bldr);
|
||||
enqueueFanout(vertex, insert_queue, srch_pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Genclks::enqueueFanin(Vertex *vertex,
|
||||
VertexQueue &insert_queue,
|
||||
SearchPred &srch_pred)
|
||||
{
|
||||
graph_->visitFanins(vertex, &srch_pred,
|
||||
[&insert_queue] (Vertex *fanin) {
|
||||
insert_queue.push(fanin);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
Genclks::enqueueFanout(Vertex *vertex,
|
||||
VertexQueue &insert_queue,
|
||||
SearchPred &srch_pred)
|
||||
{
|
||||
graph_->visitFanouts(vertex, &srch_pred,
|
||||
[&insert_queue] (Vertex *fanout) {
|
||||
insert_queue.push(fanout);
|
||||
});
|
||||
}
|
||||
|
||||
Tag *
|
||||
Genclks::makeTag(const Clock *gclk,
|
||||
const Clock *master_clk,
|
||||
|
|
@ -637,11 +659,12 @@ Genclks::makeTag(const Clock *gclk,
|
|||
state = state->nextState();
|
||||
ExceptionStateSet *states = new ExceptionStateSet();
|
||||
states->insert(state);
|
||||
const ClkInfo *clk_info = search_->findClkInfo(
|
||||
scene, master_clk->edge(master_rf), master_pin, true, nullptr, true, nullptr,
|
||||
insert, 0.0, nullptr, min_max, nullptr);
|
||||
return search_->findTag(scene, master_rf, min_max, clk_info, false, nullptr, false,
|
||||
states, true, nullptr);
|
||||
const ClkInfo *clk_info = search_->findClkInfo(scene, master_clk->edge(master_rf),
|
||||
master_pin, true, nullptr, true,
|
||||
nullptr, insert, 0.0, nullptr,
|
||||
min_max, nullptr);
|
||||
return search_->findTag(scene, master_rf, min_max, clk_info, false, nullptr,
|
||||
false, states, true, nullptr);
|
||||
}
|
||||
|
||||
class GenClkArrivalSearchPred : public EvalPred
|
||||
|
|
@ -690,8 +713,8 @@ class GenclkSrcArrivalVisitor : public ArrivalVisitor
|
|||
{
|
||||
public:
|
||||
GenclkSrcArrivalVisitor(Clock *gclk,
|
||||
BfsFwdIterator *insert_iter,
|
||||
GenclkInfo *genclk_info,
|
||||
VertexQueue &insert_queue,
|
||||
const Mode *mode);
|
||||
GenclkSrcArrivalVisitor(const GenclkSrcArrivalVisitor &visitor);
|
||||
VertexVisitor *copy() const override;
|
||||
|
|
@ -699,7 +722,7 @@ public:
|
|||
|
||||
protected:
|
||||
Clock *gclk_;
|
||||
BfsFwdIterator *insert_iter_;
|
||||
VertexQueue &insert_queue_;
|
||||
GenclkInfo *genclk_info_;
|
||||
GenClkInsertionSearchPred srch_pred_;
|
||||
const Mode *mode_;
|
||||
|
|
@ -708,12 +731,12 @@ protected:
|
|||
};
|
||||
|
||||
GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk,
|
||||
BfsFwdIterator *insert_iter,
|
||||
GenclkInfo *genclk_info,
|
||||
VertexQueue &insert_queue,
|
||||
const Mode *mode) :
|
||||
ArrivalVisitor(mode->sta()),
|
||||
gclk_(gclk),
|
||||
insert_iter_(insert_iter),
|
||||
insert_queue_(insert_queue),
|
||||
genclk_info_(genclk_info),
|
||||
srch_pred_(gclk_, genclk_info, mode->sta()),
|
||||
mode_(mode),
|
||||
|
|
@ -725,7 +748,7 @@ GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk,
|
|||
GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(const GenclkSrcArrivalVisitor &visitor) :
|
||||
ArrivalVisitor(visitor),
|
||||
gclk_(visitor.gclk_),
|
||||
insert_iter_(visitor.insert_iter_),
|
||||
insert_queue_(visitor.insert_queue_),
|
||||
genclk_info_(visitor.genclk_info_),
|
||||
srch_pred_(visitor.gclk_, visitor.genclk_info_, this),
|
||||
mode_(visitor.mode_),
|
||||
|
|
@ -748,24 +771,28 @@ GenclkSrcArrivalVisitor::visit(Vertex *vertex)
|
|||
tag_bldr_->init(vertex);
|
||||
has_fanin_one_ = graph_->hasFaninOne(vertex);
|
||||
genclks_->copyGenClkSrcPaths(vertex, tag_bldr_);
|
||||
visitFaninPaths(vertex);
|
||||
// Propagate beyond the clock tree to reach generated clk roots.
|
||||
insert_iter_->enqueueAdjacentVertices(vertex, &srch_pred_, mode_);
|
||||
visitFaninPaths(vertex, true);
|
||||
search_->setVertexArrivals(vertex, tag_bldr_);
|
||||
// Propagate beyond the clock tree to reach generated clk roots.
|
||||
genclks_->enqueueFanout(vertex, insert_queue_, srch_pred_);
|
||||
}
|
||||
|
||||
void
|
||||
Genclks::findSrcArrivals(Clock *gclk,
|
||||
BfsFwdIterator &insert_iter,
|
||||
GenclkInfo *genclk_info)
|
||||
GenclkInfo *genclk_info,
|
||||
VertexQueue &insert_queue)
|
||||
{
|
||||
GenClkArrivalSearchPred eval_pred(gclk, this);
|
||||
GenclkSrcArrivalVisitor arrival_visitor(gclk, &insert_iter, genclk_info, mode_);
|
||||
GenclkSrcArrivalVisitor arrival_visitor(gclk, genclk_info, insert_queue, mode_);
|
||||
arrival_visitor.init(true, false, &eval_pred);
|
||||
// This cannot restrict the search level because loops in the clock tree
|
||||
// can circle back to the generated clock src pin.
|
||||
// Parallel visit is slightly slower (at last check).
|
||||
insert_iter.visit(levelize_->maxLevel(), &arrival_visitor);
|
||||
while (!insert_queue.empty()) {
|
||||
Vertex *vertex = insert_queue.front();
|
||||
insert_queue.pop();
|
||||
arrival_visitor.visit(vertex);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy generated clock source paths to tag_bldr.
|
||||
|
|
@ -886,15 +913,15 @@ void
|
|||
Genclks::deleteGenclkSrcPaths(Clock *gclk)
|
||||
{
|
||||
GenclkInfo *genclk_info = genclkInfo(gclk);
|
||||
GenClkInsertionSearchPred srch_pred(gclk, genclk_info, mode_->sta());
|
||||
BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this);
|
||||
VertexQueue insert_queue;
|
||||
GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this);
|
||||
FilterPath *src_filter = genclk_info->srcFilter();
|
||||
seedSrcPins(gclk, src_filter, insert_iter);
|
||||
GenClkArrivalSearchPred eval_pred(gclk, this);
|
||||
while (insert_iter.hasNext()) {
|
||||
Vertex *vertex = insert_iter.next();
|
||||
seedSrcPins(gclk, src_filter, insert_queue, srch_pred);
|
||||
while (!insert_queue.empty()) {
|
||||
Vertex *vertex = insert_queue.front();
|
||||
insert_queue.pop();
|
||||
search_->deletePaths(vertex);
|
||||
insert_iter.enqueueAdjacentVertices(vertex, &srch_pred, mode_);
|
||||
enqueueFanout(vertex, insert_queue, srch_pred);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -42,8 +43,6 @@
|
|||
namespace sta {
|
||||
|
||||
class GenclkInfo;
|
||||
class BfsFwdIterator;
|
||||
class BfsBkwdIterator;
|
||||
class SearchPred;
|
||||
class TagGroupBldr;
|
||||
|
||||
|
|
@ -59,6 +58,7 @@ public:
|
|||
using GenclkInfoMap = std::map<Clock*, GenclkInfo*>;
|
||||
using GenclkSrcPathMap = std::map<ClockPinPair, std::vector<Path>, ClockPinPairLess>;
|
||||
using VertexGenclkSrcPathsMap = std::map<Vertex*, std::vector<const Path*>, VertexIdLess>;
|
||||
using VertexQueue = std::queue<Vertex*>;
|
||||
|
||||
class Genclks : public StaState
|
||||
{
|
||||
|
|
@ -102,18 +102,20 @@ private:
|
|||
void recordSrcPaths(Clock *gclk);
|
||||
void findInsertionDelays(Clock *gclk);
|
||||
void seedClkVertices(Clock *clk,
|
||||
BfsBkwdIterator &iter,
|
||||
VertexSet &dfanins);
|
||||
VertexQueue &fanin_queue,
|
||||
VertexSet &fanins,
|
||||
SearchPred &srch_pred);
|
||||
size_t srcPathIndex(const RiseFall *clk_rf,
|
||||
const MinMax *min_max) const;
|
||||
bool matchesSrcFilter(Path *path,
|
||||
const Clock *gclk) const;
|
||||
void seedSrcPins(Clock *gclk,
|
||||
FilterPath *src_filter,
|
||||
BfsFwdIterator &insert_iter);
|
||||
VertexQueue &insert_queue,
|
||||
SearchPred &srch_pred);
|
||||
void findSrcArrivals(Clock *gclk,
|
||||
BfsFwdIterator &insert_iter,
|
||||
GenclkInfo *genclk_info);
|
||||
GenclkInfo *genclk_info,
|
||||
VertexQueue &insert_queue);
|
||||
FilterPath *makeSrcFilter(Clock *gclk,
|
||||
Sdc *sdc);
|
||||
void deleteGenClkInfo();
|
||||
|
|
@ -125,9 +127,13 @@ private:
|
|||
Arrival insert,
|
||||
Scene *scene,
|
||||
const MinMax *min_max);
|
||||
void seedSrcPins(Clock *clk,
|
||||
BfsBkwdIterator &iter);
|
||||
void findInsertionDelay(Clock *gclk);
|
||||
void enqueueFanin(Vertex *vertex,
|
||||
VertexQueue &insert_queue,
|
||||
SearchPred &srch_pred);
|
||||
void enqueueFanout(Vertex *vertex,
|
||||
VertexQueue &insert_queue,
|
||||
SearchPred &srch_pred);
|
||||
GenclkInfo *makeGenclkInfo(Clock *gclk);
|
||||
FilterPath *srcFilter(Clock *gclk);
|
||||
void findFanin(Clock *gclk,
|
||||
|
|
@ -147,6 +153,8 @@ private:
|
|||
GenclkSrcPathMap genclk_src_paths_;
|
||||
GenclkInfoMap genclk_info_map_;
|
||||
VertexGenclkSrcPathsMap vertex_src_paths_map_;
|
||||
|
||||
friend class GenclkSrcArrivalVisitor;
|
||||
};
|
||||
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -359,16 +359,7 @@ Latches::latchOutArrival(const Path *data_path,
|
|||
arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge,
|
||||
false, min_max, dcalc_ap, sdc);
|
||||
q_arrival = delaySum(data_path->arrival(), arc_delay, this);
|
||||
// Copy the data tag but remove the drprClkPath.
|
||||
// Levelization does not traverse latch D->Q edges, so in some cases
|
||||
// level(Q) < level(D)
|
||||
// Note that
|
||||
// level(crprClkPath(data)) < level(D)
|
||||
// The danger is that
|
||||
// level(crprClkPath(data)) == level(Q)
|
||||
// or some other downstream vertex.
|
||||
// This can lead to data races when finding arrivals at the same level
|
||||
// use multiple threads.
|
||||
// Copy the data tag but remove the crprClkPath.
|
||||
// Kill the crprClklPath to be safe.
|
||||
const ClkInfo *data_clk_info = data_path->clkInfo(this);
|
||||
const ClkInfo *q_clk_info =
|
||||
|
|
|
|||
|
|
@ -135,7 +135,6 @@ Levelize::findLevels()
|
|||
findBackEdges();
|
||||
VertexSeq topo_sorted = findTopologicalOrder();
|
||||
assignLevels(topo_sorted);
|
||||
ensureLatchLevels();
|
||||
|
||||
// Set level of stranded vertices (constants) to zero.
|
||||
VertexIterator vertex_iter2(graph_);
|
||||
|
|
@ -188,9 +187,7 @@ Levelize::isRoot(Vertex *vertex)
|
|||
if (searchThru(edge))
|
||||
return false;
|
||||
}
|
||||
// Levelize bidirect driver as if it was a fanout of the bidirect load.
|
||||
return !(graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin())
|
||||
&& vertex->isBidirectDriver());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -357,8 +354,6 @@ Levelize::findTopologicalOrder()
|
|||
Vertex *to_vertex = edge->to(graph_);
|
||||
if (searchThru(edge))
|
||||
in_degree[to_vertex] += 1;
|
||||
if (edge->role() == TimingRole::latchDtoQ())
|
||||
latch_d_to_q_edges_.insert(edge);
|
||||
}
|
||||
// Levelize bidirect driver as if it was a fanout of the bidirect load.
|
||||
const Pin *pin = vertex->pin();
|
||||
|
|
@ -510,27 +505,6 @@ Levelize::assignLevels(VertexSeq &topo_sorted)
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// Make sure latch D input level is not the same as the Q level.
|
||||
// This is because the Q arrival depends on the D arrival and
|
||||
// to find them in parallel they have to be scheduled separately
|
||||
// to avoid a race condition.
|
||||
void
|
||||
Levelize::ensureLatchLevels()
|
||||
{
|
||||
for (Edge *edge : latch_d_to_q_edges_) {
|
||||
Vertex *from = edge->from(graph_);
|
||||
Vertex *to = edge->to(graph_);
|
||||
if (from->level() == to->level()) {
|
||||
Level adjusted_level = from->level() + level_space_;
|
||||
debugPrint(debug_, "levelize", 2, "latch {} {} (adjusted {}) -> {} {}",
|
||||
from->to_string(this), from->level(), adjusted_level,
|
||||
to->to_string(this), to->level());
|
||||
setLevel(from, adjusted_level);
|
||||
}
|
||||
}
|
||||
latch_d_to_q_edges_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
Levelize::setLevel(Vertex *vertex,
|
||||
Level level)
|
||||
|
|
@ -607,7 +581,6 @@ Levelize::relevelize()
|
|||
EdgeSeq path;
|
||||
visit(vertex, nullptr, vertex->level(), 1, path_vertices, path);
|
||||
}
|
||||
ensureLatchLevels();
|
||||
levels_valid_ = true;
|
||||
relevelize_from_.clear();
|
||||
}
|
||||
|
|
@ -638,18 +611,6 @@ Levelize::visit(Vertex *vertex,
|
|||
visit(to_vertex, edge, level + level_space, level_space, path_vertices,
|
||||
path);
|
||||
}
|
||||
|
||||
const TimingRole *role = edge->role();
|
||||
if (role->isLatchDtoQ())
|
||||
latch_d_to_q_edges_.insert(edge);
|
||||
if (role->isLatchEnToQ()) {
|
||||
VertexInEdgeIterator edge_iter2(to_vertex, graph_);
|
||||
while (edge_iter2.hasNext()) {
|
||||
Edge *edge2 = edge_iter2.next();
|
||||
if (edge2->role()->isLatchDtoQ())
|
||||
latch_d_to_q_edges_.insert(edge2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Levelize bidirect driver as if it was a fanout of the bidirect load.
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ protected:
|
|||
EdgeSeq &path);
|
||||
EdgeSeq *loopEdges(EdgeSeq &path,
|
||||
Edge *closing_edge);
|
||||
void ensureLatchLevels();
|
||||
void findBackEdges();
|
||||
EdgeSet findBackEdges(EdgeSeq &path,
|
||||
FindBackEdgesStack &stack);
|
||||
|
|
@ -113,7 +112,6 @@ protected:
|
|||
GraphLoopSeq loops_;
|
||||
EdgeSet loop_edges_;
|
||||
EdgeSet disabled_loop_edges_;
|
||||
EdgeSet latch_d_to_q_edges_;
|
||||
LevelizeObserver *observer_{nullptr};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -238,6 +238,28 @@ PathEnum::reportDiversionPath(Diversion *div)
|
|||
using VisitedFanins = std::set<std::pair<const Vertex *, const TimingArc *>>;
|
||||
using VertexEdge = std::pair<const Vertex *, const RiseFall *>;
|
||||
|
||||
class EnumPred : public EvalPred
|
||||
{
|
||||
public:
|
||||
EnumPred(const StaState *sta);
|
||||
bool searchThru(Edge *edge,
|
||||
const Mode *mode) const override;
|
||||
};
|
||||
|
||||
EnumPred::EnumPred(const StaState *sta) :
|
||||
EvalPred(sta)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
EnumPred::searchThru(Edge *edge,
|
||||
const Mode *mode) const
|
||||
{
|
||||
// Do not enum paths across disabled loop edges.
|
||||
return EvalPred::searchThru(edge, mode)
|
||||
&& !edge->isDisabledLoop();
|
||||
}
|
||||
|
||||
class PathEnumFaninVisitor : public PathVisitor
|
||||
{
|
||||
public:
|
||||
|
|
@ -247,6 +269,7 @@ public:
|
|||
bool unique_edges,
|
||||
PathEnum *path_enum);
|
||||
PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor);
|
||||
virtual ~PathEnumFaninVisitor();
|
||||
VertexVisitor *copy() const override;
|
||||
void visitFaninPathsThru(Path *before_div,
|
||||
Vertex *prev_vertex,
|
||||
|
|
@ -311,7 +334,7 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end,
|
|||
bool unique_pins,
|
||||
bool unique_edges,
|
||||
PathEnum *path_enum) :
|
||||
PathVisitor(path_enum),
|
||||
PathVisitor(new EnumPred(path_enum), false, path_enum),
|
||||
path_end_(path_end),
|
||||
before_div_(before_div),
|
||||
unique_pins_(unique_pins),
|
||||
|
|
@ -334,6 +357,11 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor)
|
|||
{
|
||||
}
|
||||
|
||||
PathEnumFaninVisitor::~PathEnumFaninVisitor()
|
||||
{
|
||||
delete pred_;
|
||||
}
|
||||
|
||||
VertexVisitor *
|
||||
PathEnumFaninVisitor::copy() const
|
||||
{
|
||||
|
|
@ -356,7 +384,7 @@ PathEnumFaninVisitor::visitFaninPathsThru(Path *before_div,
|
|||
prev_vertex_ = prev_vertex;
|
||||
visited_fanins_.clear();
|
||||
unique_edge_divs_.clear();
|
||||
visitFaninPaths(before_div_->vertex(this));
|
||||
visitFaninPaths(before_div_->vertex(this), true);
|
||||
|
||||
if (unique_edges_) {
|
||||
for (auto [vertex_edge, div] : unique_edge_divs_)
|
||||
|
|
@ -381,7 +409,8 @@ PathEnumFaninVisitor::visitEdge(const Pin *from_pin,
|
|||
Path *from_path = from_iter.next();
|
||||
const Mode *mode = from_path->mode(this);
|
||||
const Sdc *sdc = mode->sdc();
|
||||
if (pred_->searchFrom(from_vertex, mode) && pred_->searchThru(edge, mode)
|
||||
if (pred_->searchFrom(from_vertex, mode)
|
||||
&& pred_->searchThru(edge, mode)
|
||||
&& pred_->searchTo(to_vertex, mode)
|
||||
// Fanin paths are broken by path delay internal pin startpoints.
|
||||
&& !sdc->isPathDelayInternalFromBreak(to_pin)) {
|
||||
|
|
@ -421,6 +450,14 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *,
|
|||
Arrival & /* to_arrival */,
|
||||
const MinMax *)
|
||||
{
|
||||
debugPrint(debug_, "path_enum", 3, "visit fanin {} -> {} {} {}",
|
||||
from_path->to_string(this),
|
||||
to_vertex->to_string(this), to_rf->shortName(),
|
||||
delayAsString(
|
||||
search_->deratedDelay(
|
||||
from_vertex, arc, edge, false, from_path->minMax(this),
|
||||
from_path->dcalcAnalysisPtIndex(this), from_path->sdc(this)),
|
||||
this));
|
||||
// These paths fanin to before_div_ so we know to_vertex matches.
|
||||
if ((!unique_pins_ || from_vertex != prev_vertex_)
|
||||
&& (!unique_edges_ || from_vertex != prev_vertex_
|
||||
|
|
@ -646,10 +683,10 @@ PathEnum::makeDivertedPath(Path *path,
|
|||
Path *prev_copy = nullptr;
|
||||
while (p) {
|
||||
// prev_path made in next pass.
|
||||
Path *copy =
|
||||
new Path(p->vertex(this), p->tag(this), p->arrival(),
|
||||
// Replaced on next pass.
|
||||
p->prevPath(), p->prevEdge(this), p->prevArc(this), true, this);
|
||||
Path *copy = new Path(p->vertex(this), p->tag(this), p->arrival(),
|
||||
// Replaced on next pass.
|
||||
p->prevPath(), p->prevEdge(this), p->prevArc(this),
|
||||
true, this);
|
||||
search_->saveEnumPath(copy);
|
||||
if (prev_copy)
|
||||
prev_copy->setPrevPath(copy);
|
||||
|
|
|
|||
305
search/Search.cc
305
search/Search.cc
|
|
@ -95,9 +95,9 @@ EvalPred::searchThru(Edge *edge,
|
|||
{
|
||||
const TimingRole *role = edge->role();
|
||||
return SearchPred0::searchThru(edge, mode)
|
||||
&& (sta_->variables()->dynamicLoopBreaking() || !edge->isDisabledLoop())
|
||||
&& (search_thru_latches_ || role->isLatchDtoQ()
|
||||
|| sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open);
|
||||
&& (search_thru_latches_
|
||||
|| role->isLatchDtoQ()
|
||||
|| sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -133,54 +133,56 @@ bool
|
|||
SearchThru::searchThru(Edge *edge,
|
||||
const Mode *mode) const
|
||||
{
|
||||
return EvalPred::searchThru(edge, mode) && !edge->role()->isLatchDtoQ();
|
||||
return EvalPred::searchThru(edge, mode)
|
||||
&& !edge->role()->isLatchDtoQ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// SearchAdj is mode independent. Search unless
|
||||
// disabled to break combinational loop
|
||||
// SearchAdjLoop is mode independent. Search unless
|
||||
// latch D->Q edge
|
||||
// timing check edge
|
||||
// dynamic loop breaking pending tags
|
||||
class SearchAdj : public SearchPred
|
||||
// register set/clear
|
||||
// bidirect load->driver
|
||||
class SearchAdjLoop : public SearchPred
|
||||
{
|
||||
public:
|
||||
SearchAdj(TagGroupBldr *tag_bldr,
|
||||
const StaState *sta);
|
||||
SearchAdjLoop(const StaState *sta);
|
||||
bool searchFrom(const Vertex *from_vertex) const override;
|
||||
bool searchFrom(const Vertex *from_vertex,
|
||||
const Mode *mode) const override;
|
||||
bool searchThru(Edge *edge) const override;
|
||||
bool searchThru(Edge *edge,
|
||||
const Mode *mode) const override;
|
||||
bool searchTo(const Vertex *to_vertex) const override;
|
||||
bool searchTo(const Vertex *to_vertex,
|
||||
const Mode *mode) const override;
|
||||
|
||||
protected:
|
||||
bool loopEnabled(Edge *edge) const;
|
||||
bool hasPendingLoopPaths(Edge *edge) const;
|
||||
|
||||
TagGroupBldr *tag_bldr_;
|
||||
const StaState *sta_;
|
||||
};
|
||||
|
||||
SearchAdj::SearchAdj(TagGroupBldr *tag_bldr,
|
||||
const StaState *sta) :
|
||||
SearchAdjLoop::SearchAdjLoop(const StaState *sta) :
|
||||
SearchPred(sta),
|
||||
tag_bldr_(tag_bldr),
|
||||
sta_(sta)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
SearchAdj::searchFrom(const Vertex * /* from_vertex */,
|
||||
const Mode *) const
|
||||
SearchAdjLoop::searchFrom(const Vertex *) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SearchAdj::searchThru(Edge *edge,
|
||||
const Mode *) const
|
||||
SearchAdjLoop::searchFrom(const Vertex *,
|
||||
const Mode *) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SearchAdjLoop::searchThru(Edge *edge) const
|
||||
{
|
||||
const TimingRole *role = edge->role();
|
||||
const Variables *variables = sta_->variables();
|
||||
|
|
@ -189,55 +191,57 @@ SearchAdj::searchThru(Edge *edge,
|
|||
// Register/latch preset/clr edges are disabled by default.
|
||||
|| (role == TimingRole::regSetClr()
|
||||
&& !variables->presetClrArcsEnabled())
|
||||
|| sta_->isDisabledBidirectInstPath(edge)
|
||||
|| (edge->isDisabledLoop()
|
||||
&& !(variables->dynamicLoopBreaking() && hasPendingLoopPaths(edge))));
|
||||
|| sta_->isDisabledBidirectInstPath(edge));
|
||||
}
|
||||
|
||||
bool
|
||||
SearchAdj::loopEnabled(Edge *edge) const
|
||||
SearchAdjLoop::searchThru(Edge *edge,
|
||||
const Mode *) const
|
||||
{
|
||||
return !edge->isDisabledLoop()
|
||||
|| (sta_->variables()->dynamicLoopBreaking() && hasPendingLoopPaths(edge));
|
||||
return searchThru(edge);
|
||||
}
|
||||
|
||||
bool
|
||||
SearchAdj::hasPendingLoopPaths(Edge *edge) const
|
||||
{
|
||||
if (tag_bldr_ && tag_bldr_->hasLoopTag()) {
|
||||
const Graph *graph = sta_->graph();
|
||||
Search *search = sta_->search();
|
||||
Vertex *from_vertex = edge->from(graph);
|
||||
TagGroup *prev_tag_group = search->tagGroup(from_vertex);
|
||||
for (auto const [from_tag, path_index] : tag_bldr_->pathIndexMap()) {
|
||||
if (from_tag->isLoop()) {
|
||||
// Loop false path exceptions apply to rise/fall edges so to_rf
|
||||
// does not matter.
|
||||
Tag *to_tag = search->thruTag(from_tag, edge, RiseFall::rise(), nullptr);
|
||||
if (to_tag
|
||||
&& (prev_tag_group == nullptr || !prev_tag_group->hasTag(from_tag)))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
SearchAdj::searchTo(const Vertex * /* to_vertex */,
|
||||
const Mode *) const
|
||||
SearchAdjLoop::searchTo(const Vertex *) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SearchAdjLoop::searchTo(const Vertex *,
|
||||
const Mode *) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// SearchAdj is mode independent. Search unless
|
||||
// SearchAdjLoop but not thru disabled loop edges.
|
||||
class SearchAdj : public SearchAdjLoop
|
||||
{
|
||||
public:
|
||||
SearchAdj(const StaState *sta);
|
||||
bool searchThru(Edge *edge) const override;
|
||||
};
|
||||
|
||||
SearchAdj::SearchAdj(const StaState *sta) :
|
||||
SearchAdjLoop(sta)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
SearchAdj::searchThru(Edge *edge) const
|
||||
{
|
||||
return SearchAdjLoop::searchThru(edge)
|
||||
&& !edge->isDisabledLoop();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
Search::Search(StaState *sta) :
|
||||
StaState(sta),
|
||||
|
||||
search_thru_(new SearchThru(this)),
|
||||
search_adj_(new SearchAdj(nullptr,
|
||||
this)),
|
||||
search_adj_(new SearchAdj(this)),
|
||||
eval_pred_(new EvalPred(this)),
|
||||
|
||||
invalid_arrivals_(makeVertexSet(this)),
|
||||
|
|
@ -247,9 +251,7 @@ Search::Search(StaState *sta) :
|
|||
arrival_visitor_(new ArrivalVisitor(this)),
|
||||
|
||||
invalid_requireds_(makeVertexSet(this)),
|
||||
required_iter_(new BfsBkwdIterator(BfsIndex::required,
|
||||
search_adj_,
|
||||
this)),
|
||||
required_iter_(new BfsBkwdIterator(BfsIndex::required, search_adj_, this)),
|
||||
|
||||
invalid_tns_(makeVertexSet(this)),
|
||||
clk_info_set_(new ClkInfoSet(ClkInfoLess(this))),
|
||||
|
|
@ -261,8 +263,8 @@ Search::Search(StaState *sta) :
|
|||
tag_group_capacity_(tag_capacity_),
|
||||
tag_groups_(new TagGroup *[tag_group_capacity_]),
|
||||
tag_group_set_(new TagGroupSet(tag_group_capacity_)),
|
||||
pending_latch_outputs_(makeVertexSet(this)),
|
||||
pending_clk_endpoints_(makeVertexSet(this)),
|
||||
postponed_arrivals_(makeVertexSet(this)),
|
||||
postponed_clk_endpoints_(makeVertexSet(this)),
|
||||
endpoints_(makeVertexSet(this)),
|
||||
invalid_endpoints_(makeVertexSet(this)),
|
||||
|
||||
|
|
@ -326,8 +328,8 @@ Search::clear()
|
|||
deletePathGroups();
|
||||
deletePaths();
|
||||
deleteTags();
|
||||
clearPendingLatchOutputs();
|
||||
pending_clk_endpoints_.clear();
|
||||
postponed_arrivals_.clear();
|
||||
postponed_clk_endpoints_.clear();
|
||||
deleteFilter();
|
||||
found_downstream_clk_pins_ = false;
|
||||
}
|
||||
|
|
@ -644,18 +646,23 @@ Search::findFilteredArrivals(bool thru_latches)
|
|||
// Search always_to_endpoint to search from exisiting arrivals at
|
||||
// fanin startpoints to reach -thru/-to endpoints.
|
||||
arrival_visitor_->init(true, false, eval_pred_);
|
||||
// Iterate until data arrivals at all latches stop changing.
|
||||
postpone_latch_outputs_ = true;
|
||||
enqueuePendingClkFanouts();
|
||||
for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs());
|
||||
bool have_pending_latch_outputs = false;
|
||||
// Iterate until data arrivals at all latches stop changing.
|
||||
for (int pass = 1;
|
||||
pass == 1 || (thru_latches && have_pending_latch_outputs);
|
||||
pass++) {
|
||||
if (thru_latches)
|
||||
enqueuePendingLatchOutputs();
|
||||
debugPrint(debug_, "search", 1, "find arrivals pass {}", pass);
|
||||
|
||||
int arrival_count = arrival_iter_->visitParallel(max_level, arrival_visitor_);
|
||||
deleteTagsPrev();
|
||||
debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count);
|
||||
postpone_latch_outputs_ = false;
|
||||
|
||||
have_pending_latch_outputs = !postponed_arrivals_.empty();
|
||||
for (Vertex *latch_output : postponed_arrivals_)
|
||||
arrival_visitor_->visit(latch_output, true);
|
||||
postponed_arrivals_.clear();
|
||||
|
||||
deleteTagsPrev();
|
||||
}
|
||||
arrivals_exist_ = true;
|
||||
}
|
||||
|
|
@ -983,57 +990,39 @@ Search::findAllArrivals(bool thru_latches,
|
|||
if (!clks_only)
|
||||
enqueuePendingClkFanouts();
|
||||
arrival_visitor_->init(false, clks_only, eval_pred_);
|
||||
bool have_pending_latch_outputs = false;
|
||||
// Iterate until data arrivals at all latches stop changing.
|
||||
postpone_latch_outputs_ = true;
|
||||
for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs());
|
||||
for (int pass = 1;
|
||||
pass == 1 || (thru_latches && have_pending_latch_outputs);
|
||||
pass++) {
|
||||
enqueuePendingLatchOutputs();
|
||||
debugPrint(debug_, "search", 1, "find arrivals pass {}", pass);
|
||||
|
||||
findArrivals1(levelize_->maxLevel());
|
||||
if (pass > 2)
|
||||
postpone_latch_outputs_ = false;
|
||||
|
||||
have_pending_latch_outputs = !postponed_arrivals_.empty();
|
||||
for (Vertex *latch_output : postponed_arrivals_)
|
||||
arrival_visitor_->visit(latch_output, true);
|
||||
postponed_arrivals_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Search::havePendingLatchOutputs()
|
||||
{
|
||||
return !pending_latch_outputs_.empty();
|
||||
}
|
||||
|
||||
void
|
||||
Search::clearPendingLatchOutputs()
|
||||
{
|
||||
pending_latch_outputs_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
Search::enqueuePendingLatchOutputs()
|
||||
{
|
||||
for (Vertex *latch_vertex : pending_latch_outputs_) {
|
||||
debugPrint(debug_, "search", 2, "enqueue latch output {}",
|
||||
latch_vertex->to_string(this));
|
||||
arrival_iter_->enqueue(latch_vertex);
|
||||
}
|
||||
clearPendingLatchOutputs();
|
||||
}
|
||||
|
||||
// Pick up where the search stopped at the clock network boundary.
|
||||
void
|
||||
Search::enqueuePendingClkFanouts()
|
||||
{
|
||||
for (Vertex *vertex : pending_clk_endpoints_) {
|
||||
for (Vertex *vertex : postponed_clk_endpoints_) {
|
||||
debugPrint(debug_, "search", 2, "enqueue clk fanout {}",
|
||||
vertex->to_string(this));
|
||||
arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_);
|
||||
}
|
||||
pending_clk_endpoints_.clear();
|
||||
postponed_clk_endpoints_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
Search::postponeClkFanouts(Vertex *vertex)
|
||||
{
|
||||
LockGuard lock(pending_clk_endpoints_lock_);
|
||||
pending_clk_endpoints_.insert(vertex);
|
||||
LockGuard lock(postponed_clk_endpoints_lock_);
|
||||
postponed_clk_endpoints_.insert(vertex);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1103,7 +1092,7 @@ ArrivalVisitor::init0()
|
|||
{
|
||||
tag_bldr_ = new TagGroupBldr(true, this);
|
||||
tag_bldr_no_crpr_ = new TagGroupBldr(false, this);
|
||||
adj_pred_ = new SearchAdj(tag_bldr_, this);
|
||||
search_adj_ = new SearchAdjLoop(this);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1127,14 +1116,14 @@ void
|
|||
ArrivalVisitor::copyState(const StaState *sta)
|
||||
{
|
||||
StaState::copyState(sta);
|
||||
adj_pred_->copyState(sta);
|
||||
search_adj_->copyState(sta);
|
||||
}
|
||||
|
||||
ArrivalVisitor::~ArrivalVisitor()
|
||||
{
|
||||
delete tag_bldr_;
|
||||
delete tag_bldr_no_crpr_;
|
||||
delete adj_pred_;
|
||||
delete search_adj_;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1145,16 +1134,27 @@ ArrivalVisitor::setAlwaysToEndpoints(bool to_endpoints)
|
|||
|
||||
void
|
||||
ArrivalVisitor::visit(Vertex *vertex)
|
||||
{
|
||||
if (network_->isLatchOutput(vertex->pin()))
|
||||
search_->postponeArrivals(vertex);
|
||||
else
|
||||
visit(vertex, false);
|
||||
}
|
||||
|
||||
void
|
||||
ArrivalVisitor::visit(Vertex *vertex,
|
||||
bool with_latch_edges)
|
||||
{
|
||||
debugPrint(debug_, "search", 2, "find arrivals {}",
|
||||
vertex->to_string(this));
|
||||
|
||||
Pin *pin = vertex->pin();
|
||||
tag_bldr_->init(vertex);
|
||||
has_fanin_one_ = graph_->hasFaninOne(vertex);
|
||||
if (crpr_active_ && !has_fanin_one_)
|
||||
tag_bldr_no_crpr_->init(vertex);
|
||||
|
||||
visitFaninPaths(vertex);
|
||||
visitFaninPaths(vertex, with_latch_edges);
|
||||
if (crpr_active_
|
||||
&& search_->crprPathPruningEnabled()
|
||||
// No crpr for ideal clocks.
|
||||
|
|
@ -1170,15 +1170,25 @@ ArrivalVisitor::visit(Vertex *vertex)
|
|||
// previous eval pass enqueue the latch outputs to be re-evaled on the
|
||||
// next pass.
|
||||
if (arrivals_changed && network_->isLatchData(pin))
|
||||
search_->enqueueLatchDataOutputs(vertex);
|
||||
search_->postponeLatchDataOutputs(vertex);
|
||||
|
||||
if ((always_to_endpoints_ || arrivals_changed)) {
|
||||
if (clks_only_ && vertex->isRegClk()) {
|
||||
debugPrint(debug_, "search", 3, "postponing clk fanout");
|
||||
search_->postponeClkFanouts(vertex);
|
||||
}
|
||||
else
|
||||
search_->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_);
|
||||
else {
|
||||
graph_->visitFanoutEdges(vertex, search_adj_,
|
||||
[this] (Edge *edge,
|
||||
Vertex *fanout) {
|
||||
if (edge->isDisabledLoop()) {
|
||||
if (hasPendingLoopPaths(edge))
|
||||
search_->postponeArrivals(fanout);
|
||||
}
|
||||
else
|
||||
search_->arrivalIterator()->enqueue(fanout);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (arrivals_changed) {
|
||||
debugPrint(debug_, "search", 4, "arrivals changed");
|
||||
|
|
@ -1188,6 +1198,26 @@ ArrivalVisitor::visit(Vertex *vertex)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ArrivalVisitor::hasPendingLoopPaths(Edge *edge) const
|
||||
{
|
||||
if (tag_bldr_ && tag_bldr_->hasLoopTag()) {
|
||||
Vertex *from_vertex = edge->from(graph_);
|
||||
TagGroup *prev_tag_group = search_->tagGroup(from_vertex);
|
||||
for (auto const [from_tag, path_index] : tag_bldr_->pathIndexMap()) {
|
||||
if (from_tag->isLoop()) {
|
||||
// Loop false path exceptions apply to rise/fall edges so to_rf
|
||||
// does not matter.
|
||||
Tag *to_tag = search_->thruTag(from_tag, edge, RiseFall::rise(), nullptr);
|
||||
if (to_tag
|
||||
&& (prev_tag_group == nullptr || !prev_tag_group->hasTag(from_tag)))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ArrivalVisitor::seedArrivals(Vertex *vertex)
|
||||
{
|
||||
|
|
@ -1388,24 +1418,24 @@ ArrivalVisitor::pruneCrprArrivals()
|
|||
}
|
||||
|
||||
void
|
||||
Search::enqueueLatchDataOutputs(Vertex *vertex)
|
||||
Search::postponeLatchDataOutputs(Vertex *latch_data)
|
||||
{
|
||||
VertexOutEdgeIterator edge_iter(vertex, graph_);
|
||||
VertexOutEdgeIterator edge_iter(latch_data, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *edge = edge_iter.next();
|
||||
if (edge->role() == TimingRole::latchDtoQ()) {
|
||||
Vertex *out_vertex = edge->to(graph_);
|
||||
LockGuard lock(pending_latch_outputs_lock_);
|
||||
pending_latch_outputs_.insert(out_vertex);
|
||||
LockGuard lock(postponed_arrivals_lock_);
|
||||
postponed_arrivals_.insert(out_vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Search::enqueueLatchOutput(Vertex *vertex)
|
||||
Search::postponeArrivals(Vertex *vertex)
|
||||
{
|
||||
LockGuard lock(pending_latch_outputs_lock_);
|
||||
pending_latch_outputs_.insert(vertex);
|
||||
LockGuard lock(postponed_arrivals_lock_);
|
||||
postponed_arrivals_.insert(vertex);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1948,12 +1978,16 @@ PathVisitor::~PathVisitor()
|
|||
}
|
||||
|
||||
void
|
||||
PathVisitor::visitFaninPaths(Vertex *to_vertex)
|
||||
PathVisitor::visitFaninPaths(Vertex *to_vertex,
|
||||
bool with_latch_edges)
|
||||
{
|
||||
VertexInEdgeIterator edge_iter(to_vertex, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *edge = edge_iter.next();
|
||||
if (!edge->role()->isTimingCheck()) {
|
||||
const TimingRole *role = edge->role();
|
||||
if (!(role->isTimingCheck()
|
||||
|| (role == TimingRole::latchDtoQ()
|
||||
&& !with_latch_edges))) {
|
||||
Vertex *from_vertex = edge->from(graph_);
|
||||
const Pin *from_pin = from_vertex->pin();
|
||||
const Pin *to_pin = to_vertex->pin();
|
||||
|
|
@ -1994,7 +2028,8 @@ PathVisitor::visitEdge(const Pin *from_pin,
|
|||
Path *from_path = from_iter.next();
|
||||
const Mode *mode = from_path->mode(this);
|
||||
if (mode == prev_mode
|
||||
|| (pred_->searchFrom(from_vertex, mode) && pred_->searchThru(edge, mode)
|
||||
|| (pred_->searchFrom(from_vertex, mode)
|
||||
&& pred_->searchThru(edge, mode)
|
||||
&& pred_->searchTo(to_vertex, mode))) {
|
||||
prev_mode = mode;
|
||||
const MinMax *min_max = from_path->minMax(this);
|
||||
|
|
@ -2068,9 +2103,8 @@ PathVisitor::visitFromPath(const Pin *from_pin,
|
|||
if (gclk) {
|
||||
Genclks *genclks = mode->genclks();
|
||||
VertexSet *fanins = genclks->fanins(gclk);
|
||||
// Note: encountering a latch d->q edge means find the
|
||||
// latch feedback edges, but they are referenced for
|
||||
// other edges in the gen clk fanout.
|
||||
// Note: encountering a latch d->q edge means we need to find
|
||||
// latch feedback edges.
|
||||
if (role == TimingRole::latchDtoQ())
|
||||
genclks->findLatchFdbkEdges(gclk);
|
||||
EdgeSet &fdbk_edges = genclks->latchFdbkEdges(gclk);
|
||||
|
|
@ -2140,33 +2174,12 @@ PathVisitor::visitFromPath(const Pin *from_pin,
|
|||
}
|
||||
else if (edge->role() == TimingRole::latchDtoQ()) {
|
||||
if (min_max == MinMax::max() && clk) {
|
||||
bool postponed = false;
|
||||
if (search_->postponeLatchOutputs()) {
|
||||
const Path *from_clk_path = from_clk_info->crprClkPath(this);
|
||||
if (from_clk_path) {
|
||||
Vertex *d_clk_vertex = from_clk_path->vertex(this);
|
||||
Level d_clk_level = d_clk_vertex->level();
|
||||
Level q_level = to_vertex->level();
|
||||
if (d_clk_level >= q_level) {
|
||||
// Crpr clk path on latch data input is required to find Q
|
||||
// arrival. If the data clk path level is >= Q level the
|
||||
// crpr clk path prev_path pointers are not complete.
|
||||
debugPrint(debug_, "search", 3, "postponed latch eval {} {} -> {} {}",
|
||||
d_clk_level, d_clk_vertex->to_string(this),
|
||||
edge->to_string(this), q_level);
|
||||
postponed = true;
|
||||
search_->enqueueLatchOutput(to_vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!postponed) {
|
||||
arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max,
|
||||
dcalc_ap, sdc);
|
||||
latches_->latchOutArrival(from_path, arc, edge, to_tag, arc_delay,
|
||||
to_arrival);
|
||||
if (to_tag)
|
||||
to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_);
|
||||
}
|
||||
arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max,
|
||||
dcalc_ap, sdc);
|
||||
latches_->latchOutArrival(from_path, arc, edge, to_tag, arc_delay,
|
||||
to_arrival);
|
||||
if (to_tag)
|
||||
to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_);
|
||||
}
|
||||
}
|
||||
else if (from_tag->isClock()) {
|
||||
|
|
|
|||
|
|
@ -544,6 +544,8 @@ Sta::clearNonSdc()
|
|||
for (Mode *mode : modes_) {
|
||||
mode->clkNetwork()->clkPinsInvalid();
|
||||
mode->sim()->clear();
|
||||
// ref_pin edges are owned by the graph deleted below; force a rebuild.
|
||||
mode->sdc()->inputDelayRefPinEdgesInvalid();
|
||||
}
|
||||
search_->clear();
|
||||
|
||||
|
|
@ -3990,7 +3992,7 @@ Sta::setPortExtPinCap(const Port *port,
|
|||
for (const MinMax *mm : min_max->range())
|
||||
sdc->setPortExtPinCap(port, rf1, mm, cap);
|
||||
}
|
||||
delaysInvalidFromFanin(port);
|
||||
delaysInvalidFrom(port);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -4045,7 +4047,7 @@ Sta::setPortExtWireCap(const Port *port,
|
|||
for (const MinMax *mm : min_max->range())
|
||||
sdc->setPortExtWireCap(port, rf1, mm, cap);
|
||||
}
|
||||
delaysInvalidFromFanin(port);
|
||||
delaysInvalidFrom(port);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -4063,7 +4065,7 @@ Sta::setPortExtFanout(const Port *port,
|
|||
{
|
||||
for (const MinMax *mm : min_max->range())
|
||||
sdc->setPortExtFanout(port, mm, fanout);
|
||||
delaysInvalidFromFanin(port);
|
||||
delaysInvalidFrom(port);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -4432,9 +4434,15 @@ Sta::makeNet(const char *name,
|
|||
{
|
||||
NetworkEdit *network = networkCmdEdit();
|
||||
std::string escaped = escapeBrackets(name, network);
|
||||
Net *net = network->makeNet(escaped, parent);
|
||||
// Sta notification unnecessary.
|
||||
return net;
|
||||
if (network->findNet(parent, escaped)) {
|
||||
report_->warn(1557, "net {} already exists.", name);
|
||||
return nullptr;
|
||||
}
|
||||
else {
|
||||
Net *net = network->makeNet(escaped, parent);
|
||||
// Sta notification unnecessary.
|
||||
return net;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -5042,20 +5050,6 @@ Sta::delaysInvalidFrom(Vertex *vertex)
|
|||
graph_delay_calc_->delayInvalid(vertex);
|
||||
}
|
||||
|
||||
void
|
||||
Sta::delaysInvalidFromFanin(const Port *port)
|
||||
{
|
||||
if (graph_) {
|
||||
Instance *top_inst = network_->topInstance();
|
||||
Pin *pin = network_->findPin(top_inst, port);
|
||||
Vertex *vertex, *bidirect_drvr_vertex;
|
||||
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
||||
delaysInvalidFromFanin(vertex);
|
||||
if (bidirect_drvr_vertex)
|
||||
delaysInvalidFromFanin(bidirect_drvr_vertex);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Sta::delaysInvalidFromFanin(const Pin *pin)
|
||||
{
|
||||
|
|
@ -5095,9 +5089,11 @@ Sta::delaysInvalidFromFanin(Vertex *vertex)
|
|||
VertexInEdgeIterator edge_iter(vertex, graph_);
|
||||
while (edge_iter.hasNext()) {
|
||||
Edge *edge = edge_iter.next();
|
||||
Vertex *from_vertex = edge->from(graph_);
|
||||
delaysInvalidFrom(from_vertex);
|
||||
search_->requiredInvalid(from_vertex);
|
||||
if (edge->isWire()) {
|
||||
Vertex *from_vertex = edge->from(graph_);
|
||||
delaysInvalidFrom(from_vertex);
|
||||
search_->requiredInvalid(from_vertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
=== before rebuild: in1 (ref_pin) ===
|
||||
Startpoint: in1 (input port clocked by clk)
|
||||
Endpoint: r1 (rising edge-triggered flip-flop clocked by clk)
|
||||
Path Group: clk
|
||||
Path Type: max
|
||||
|
||||
Delay Time Description
|
||||
---------------------------------------------------------
|
||||
0.00 0.00 clock clk (rise edge)
|
||||
0.00 0.00 clock network delay (ideal)
|
||||
100.00 100.00 ^ input external delay
|
||||
0.00 100.00 ^ in1 (in)
|
||||
0.00 100.00 ^ r1/D (DFFHQx4_ASAP7_75t_R)
|
||||
100.00 data arrival time
|
||||
|
||||
500.00 500.00 clock clk (rise edge)
|
||||
0.00 500.00 clock network delay (ideal)
|
||||
0.00 500.00 clock reconvergence pessimism
|
||||
500.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R)
|
||||
-11.41 488.59 library setup time
|
||||
488.59 data required time
|
||||
---------------------------------------------------------
|
||||
488.59 data required time
|
||||
-100.00 data arrival time
|
||||
---------------------------------------------------------
|
||||
388.59 slack (MET)
|
||||
|
||||
|
||||
=== before rebuild: in2 (control) ===
|
||||
Startpoint: in2 (input port clocked by clk)
|
||||
Endpoint: r2 (rising edge-triggered flip-flop clocked by clk)
|
||||
Path Group: clk
|
||||
Path Type: max
|
||||
|
||||
Delay Time Description
|
||||
---------------------------------------------------------
|
||||
0.00 0.00 clock clk (rise edge)
|
||||
0.00 0.00 clock network delay (ideal)
|
||||
100.00 100.00 ^ input external delay
|
||||
0.00 100.00 ^ in2 (in)
|
||||
0.00 100.00 ^ r2/D (DFFHQx4_ASAP7_75t_R)
|
||||
100.00 data arrival time
|
||||
|
||||
500.00 500.00 clock clk (rise edge)
|
||||
0.00 500.00 clock network delay (ideal)
|
||||
0.00 500.00 clock reconvergence pessimism
|
||||
500.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R)
|
||||
-13.11 486.89 library setup time
|
||||
486.89 data required time
|
||||
---------------------------------------------------------
|
||||
486.89 data required time
|
||||
-100.00 data arrival time
|
||||
---------------------------------------------------------
|
||||
386.89 slack (MET)
|
||||
|
||||
|
||||
=== after rebuild: in1 (ref_pin) ===
|
||||
Startpoint: in1 (input port clocked by clk)
|
||||
Endpoint: r1 (rising edge-triggered flip-flop clocked by clk)
|
||||
Path Group: clk
|
||||
Path Type: max
|
||||
|
||||
Delay Time Description
|
||||
---------------------------------------------------------
|
||||
0.00 0.00 clock clk (rise edge)
|
||||
0.00 0.00 clock network delay (ideal)
|
||||
100.00 100.00 ^ input external delay
|
||||
0.00 100.00 ^ in1 (in)
|
||||
0.00 100.00 ^ r1/D (DFFHQx4_ASAP7_75t_R)
|
||||
100.00 data arrival time
|
||||
|
||||
500.00 500.00 clock clk (rise edge)
|
||||
0.00 500.00 clock network delay (ideal)
|
||||
0.00 500.00 clock reconvergence pessimism
|
||||
500.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R)
|
||||
-11.41 488.59 library setup time
|
||||
488.59 data required time
|
||||
---------------------------------------------------------
|
||||
488.59 data required time
|
||||
-100.00 data arrival time
|
||||
---------------------------------------------------------
|
||||
388.59 slack (MET)
|
||||
|
||||
|
||||
=== after rebuild: in2 (control) ===
|
||||
Startpoint: in2 (input port clocked by clk)
|
||||
Endpoint: r2 (rising edge-triggered flip-flop clocked by clk)
|
||||
Path Group: clk
|
||||
Path Type: max
|
||||
|
||||
Delay Time Description
|
||||
---------------------------------------------------------
|
||||
0.00 0.00 clock clk (rise edge)
|
||||
0.00 0.00 clock network delay (ideal)
|
||||
100.00 100.00 ^ input external delay
|
||||
0.00 100.00 ^ in2 (in)
|
||||
0.00 100.00 ^ r2/D (DFFHQx4_ASAP7_75t_R)
|
||||
100.00 data arrival time
|
||||
|
||||
500.00 500.00 clock clk (rise edge)
|
||||
0.00 500.00 clock network delay (ideal)
|
||||
0.00 500.00 clock reconvergence pessimism
|
||||
500.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R)
|
||||
-13.11 486.89 library setup time
|
||||
486.89 data required time
|
||||
---------------------------------------------------------
|
||||
486.89 data required time
|
||||
-100.00 data arrival time
|
||||
---------------------------------------------------------
|
||||
386.89 slack (MET)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# set_input_delay -reference_pin must survive a graph rebuild that keeps SDC
|
||||
# (resizer-like: sta::network_changed_non_sdc deletes the graph but not the
|
||||
# constraints). The ref_pin->input graph edge is owned by the graph; its
|
||||
# existence flag (PortDelay::ref_pin_edges_exist_) must be reset on graph
|
||||
# teardown so ensureInputDelayRefPinEdges() rebuilds the edge. Otherwise in1
|
||||
# loses its arrival seeding and reports "No paths found" after the rebuild.
|
||||
# in2 is a plain input delay (no ref_pin) used as a control.
|
||||
read_liberty asap7_small.lib.gz
|
||||
read_verilog reg1_asap7.v
|
||||
link_design top
|
||||
|
||||
create_clock -name clk -period 500 {clk1 clk2 clk3}
|
||||
set_input_delay -clock clk 100 -reference_pin r2/CLK [get_ports in1]
|
||||
set_input_delay -clock clk 100 [get_ports in2]
|
||||
set_input_transition 10 {in1 in2 clk1 clk2 clk3}
|
||||
set_output_delay -clock clk 1 [get_ports out]
|
||||
|
||||
puts "=== before rebuild: in1 (ref_pin) ==="
|
||||
report_checks -from in1 -path_delay max
|
||||
puts "=== before rebuild: in2 (control) ==="
|
||||
report_checks -from in2 -path_delay max
|
||||
|
||||
sta::network_changed_non_sdc
|
||||
|
||||
puts "=== after rebuild: in1 (ref_pin) ==="
|
||||
report_checks -from in1 -path_delay max
|
||||
puts "=== after rebuild: in2 (control) ==="
|
||||
report_checks -from in2 -path_delay max
|
||||
|
|
@ -148,6 +148,7 @@ record_public_tests {
|
|||
get_lib_pins_of_objects
|
||||
get_noargs
|
||||
get_objrefs
|
||||
input_delay_ref_pin_rebuild
|
||||
liberty_arcs_one2one_1
|
||||
liberty_arcs_one2one_2
|
||||
liberty_backslash_eol
|
||||
|
|
|
|||
|
|
@ -1468,8 +1468,7 @@ VerilogReader::linkNetwork(std::string_view top_cell_name,
|
|||
while (net_name_iter->hasNext()) {
|
||||
const std::string &net_name = net_name_iter->next();
|
||||
Port *port = network_->findPort(top_cell, net_name);
|
||||
Net *net =
|
||||
bindings.ensureNetBinding(net_name, top_instance, network_);
|
||||
Net *net = bindings.ensureNetBinding(net_name, top_instance, network_);
|
||||
// Guard against repeated port name.
|
||||
if (network_->findPin(top_instance, port) == nullptr) {
|
||||
Pin *pin = network_->makePin(top_instance, port, nullptr);
|
||||
|
|
@ -1521,13 +1520,11 @@ VerilogReader::makeModuleInstBody(VerilogModule *module,
|
|||
if (assign)
|
||||
mergeAssignNet(assign, module, inst, bindings);
|
||||
if (dir->isGround()) {
|
||||
Net *net =
|
||||
bindings->ensureNetBinding(arg->netName(), inst, network_);
|
||||
Net *net = bindings->ensureNetBinding(arg->netName(), inst, network_);
|
||||
network_->addConstantNet(net, LogicValue::zero);
|
||||
}
|
||||
if (dir->isPower()) {
|
||||
Net *net =
|
||||
bindings->ensureNetBinding(arg->netName(), inst, network_);
|
||||
Net *net = bindings->ensureNetBinding(arg->netName(), inst, network_);
|
||||
network_->addConstantNet(net, LogicValue::one);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue