Merge branch 'master' into k8s-pipeline-opensta

This commit is contained in:
Joao Luis Sombrio 2026-07-08 02:15:26 -03:00
commit 34cf0af98e
No known key found for this signature in database
GPG Key ID: 2A57264ECDB2659D
94 changed files with 2919 additions and 2271 deletions

View File

@ -1,15 +1,22 @@
--- ---
description: C++ coding standards and formatting for OpenSTA description: C++ coding standards and formatting for OpenSTA
globs: ["**/*.cc", "**/*.hh", "**/*.h"] globs: ["**/*.cc", "**/*.hh", "**/*.h"]
alwaysApply: false alwaysApply: true
--- ---
# C++ Coding Standards # C++ Coding Standards
## File-internal helpers (translation-unit local)
- For helpers used only in one `.cc` file, put them in the **same namespace** as the rest of the implementation (e.g. `namespace sta { ... }`).
- Mark them **`static`** at namespace scope so they have internal linkage.
- **Do not** wrap them in an **anonymous namespace** in this project (no `namespace { ... }` file-static helpers). The user preference is `static` inside the real namespace, not a nested anonymous namespace inside `sta` or at file scope before `sta`.
## Line Width ## Line Width
- **Keep lines under 90 characters** to match `.clang-format` (ColumnLimit: 90). - **Keep lines under 90 characters** to match `.clang-format` (ColumnLimit: 90).
- Break long lines at logical points: after commas, before operators, after opening parens. - Only break a line when it would exceed 90 characters. Do not introduce unnecessary line breaks when the expression fits on one line.
- When a break is needed, break at logical points: after commas, before operators. Keep the first argument on the same line as the opening paren (do not break immediately after an opening paren).
## Naming Conventions ## Naming Conventions
@ -27,6 +34,13 @@ alwaysApply: false
- Prefer `std::string` over `char*` for string members. - Prefer `std::string` over `char*` for string members.
- Prefer pass-by-value and move for sink parameters (parameters that get stored). - 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 ## File Extensions
- C++ source: `.cc` - C++ source: `.cc`

View File

@ -25,11 +25,11 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Cache buildifier - name: Cache buildifier
id: cache-buildifier id: cache-buildifier
uses: actions/cache@v5 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: ./buildifier path: ./buildifier
key: ${{ runner.os }}-buildifier-${{ env.BUILDIFIER_VERSION }} key: ${{ runner.os }}-buildifier-${{ env.BUILDIFIER_VERSION }}

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
submodules: true submodules: true
@ -48,7 +48,7 @@ jobs:
./regression ./regression
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
with: with:
name: artifact name: artifact
@ -57,7 +57,7 @@ jobs:
retention-days: 1 retention-days: 1
- name: Upload Test Result - name: Upload Test Result
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
with: with:
name: result name: result

View File

@ -8,7 +8,7 @@ jobs:
runs-on: ${{ vars.USE_SELF_HOSTED == 'true' && 'self-hosted' || 'ubuntu-latest' }} runs-on: ${{ vars.USE_SELF_HOSTED == 'true' && 'self-hosted' || 'ubuntu-latest' }}
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Check ok files - name: Check ok files

View File

@ -11,6 +11,6 @@ jobs:
runs-on: ${{ vars.USE_SELF_HOSTED == 'true' && 'self-hosted' || 'ubuntu-latest' }} runs-on: ${{ vars.USE_SELF_HOSTED == 'true' && 'self-hosted' || 'ubuntu-latest' }}
steps: steps:
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: run security_scan_on_push - name: run security_scan_on_push
uses: The-OpenROAD-Project/actions/security_scan_on_push@main uses: The-OpenROAD-Project/actions/security_scan_on_push@main

8
BUILD
View File

@ -396,11 +396,11 @@ cc_library(
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = [ deps = [
"@cudd", "@cudd",
"@eigen", "@eigen", # keep. Is used, but with <>-includes
"@openmp", "@openmp", # keep. Is needed ? Nobody includes omp.h
"@rules_flex//flex:current_flex_toolchain", "@rules_flex//flex:current_flex_toolchain",
"@tcl_lang//:tcl", "@tcl_lang//:tcl", # keep. Is used, but with <>-includes
"@zlib", "@zlib", # keep. Is used, but with <>-includes
], ],
) )

46
Dockerfile.ubuntu24.04 Normal file
View File

@ -0,0 +1,46 @@
FROM ubuntu:24.04
LABEL author="James Cherry"
LABEL maintainer="James Cherry <cherry@parallaxsw.com>"
# Install basics
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y \
git \
wget \
cmake \
gcc \
gdb \
tcl-dev \
tcl-tclreadline \
swig \
bison \
flex \
automake \
autotools-dev \
libeigen3-dev \
libfmt-dev
# Download CUDD
RUN wget https://raw.githubusercontent.com/davidkebo/cudd/main/cudd_versions/cudd-3.0.0.tar.gz && \
tar -xvf cudd-3.0.0.tar.gz && \
rm cudd-3.0.0.tar.gz
# Build CUDD
RUN cd cudd-3.0.0 && \
mkdir ../cudd && \
./configure && \
make -j`nproc`
# Copy files and install OpenSTA
RUN mkdir OpenSTA
COPY . OpenSTA
RUN cd OpenSTA && \
rm -rf build && \
mkdir build && \
cd build && \
cmake -DCUDD_DIR=../cudd-3.0.0 .. && \
make -j`nproc`
# Run sta on entry
ENTRYPOINT ["OpenSTA/build/sta"]

View File

@ -125,18 +125,16 @@ if { ![catch {package require tclreadline}] } {
The Zlib library is an optional. If CMake finds libz, OpenSTA can The Zlib library is an optional. If CMake finds libz, OpenSTA can
read Liberty, Verilog, SDF, SPF, and SPEF files compressed with gzip. read Liberty, Verilog, SDF, SPF, and SPEF files compressed with gzip.
CUDD is a binary decision diageram (BDD) package that is used to [CUDD](https://github.com/cuddorg/cudd) is a binary decision diagram (BDD)
improve conditional timing arc handling, constant propagation, power package that is used to improve conditional timing arc handling, constant
activity propagation and spice netlist generation. propagation, power activity propagation and spice netlist generation.
CUDD is available Download and build CUDD:
[here](https://github.com/davidkebo/cudd/blob/main/cudd_versions/cudd-3.0.0.tar.gz).
Unpack and build CUDD.
``` ```
tar xvfz cudd-3.0.0.tar.gz git clone https://github.com/cuddorg/cudd.git
cd cudd-3.0.0 cd cudd
git checkout 3.0.0
./configure ./configure
make make
``` ```
@ -196,7 +194,7 @@ following command builds a Docker image.
``` ```
cd OpenSTA cd OpenSTA
docker build --file Dockerfile.ubuntu22.04 --tag opensta_ubuntu22.04 . docker build --file Dockerfile.ubuntu24.04 --tag opensta_ubuntu24.04 .
or or
docker build --file Dockerfile.centos7 --tag opensta_centos7 . docker build --file Dockerfile.centos7 --tag opensta_centos7 .
``` ```

View File

@ -29,13 +29,14 @@ set(TCL_POSSIBLE_NAMES
tcl85 tcl8.5 tcl85 tcl8.5
) )
# tcl lib path guesses. # TCL lib path guesses.
if (NOT TCL_LIB_PATHS) if (NOT TCL_LIB_PATHS)
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
file(GLOB tcl_homebrew_libs
/opt/homebrew/Cellar/tcl-tk@8/*/lib
)
set(TCL_LIB_PATHS set(TCL_LIB_PATHS
#/opt/homebrew/Cellar/tcl-tk/9.0.3/lib ${tcl_homebrew_libs}
/opt/homebrew/Cellar/tcl-tk@8/8.6.17/lib
/opt/homebrew/Cellar/tcl-tk@8/8.6.16/lib
/opt/homebrew/opt/tcl-tk/lib /usr/local/lib) /opt/homebrew/opt/tcl-tk/lib /usr/local/lib)
set(TCL_NO_DEFAULT_PATH TRUE) set(TCL_NO_DEFAULT_PATH TRUE)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")

View File

@ -31,6 +31,7 @@
// slew voltage is matched instead of y20 in eqn 12. // slew voltage is matched instead of y20 in eqn 12.
#include "DmpCeff.hh" #include "DmpCeff.hh"
#include <Eigen/Dense>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@ -137,20 +138,23 @@ DmpAlg::init(const LibertyLibrary *drvr_library,
void void
DmpAlg::findDriverParams(double ceff) DmpAlg::findDriverParams(double ceff)
{ {
Eigen::Vector3d x = Eigen::Vector3d::Zero();
if (nr_order_ == 3) if (nr_order_ == 3)
x_[DmpParam::ceff] = ceff; x[DmpParam::ceff] = ceff;
auto [t_vth, t_vl, slew] = gateDelays(ceff); auto [t_vth, t_vl, slew] = gateDelays(ceff);
// Scale slew to 0-100% // Scale slew to 0-100%
double dt = slew / (vh_ - vl_); double dt = slew / (vh_ - vl_);
double t0 = t_vth + std::log(1.0 - vth_) * rd_ * ceff - vth_ * dt; double t0 = t_vth + std::log(1.0 - vth_) * rd_ * ceff - vth_ * dt;
x_[DmpParam::dt] = dt; x[DmpParam::dt] = dt;
x_[DmpParam::t0] = t0; x[DmpParam::t0] = t0;
newtonRaphson(); newtonRaphson(x);
t0_ = x_[DmpParam::t0]; t0_ = x[DmpParam::t0];
dt_ = x_[DmpParam::dt]; dt_ = x[DmpParam::dt];
if (nr_order_ == 3)
ceff_ = x[DmpParam::ceff];
debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}", debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}",
units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_), units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_),
units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); units_->capacitanceUnit()->asString(ceff_));
if (debug_->check("dmp_ceff", 4)) if (debug_->check("dmp_ceff", 4))
showVo(); showVo();
} }
@ -241,21 +245,21 @@ DmpAlg::y0dcl(double t,
} }
void void
DmpAlg::showX() DmpAlg::showX(const Eigen::Vector3d &x)
{ {
for (int i = 0; i < nr_order_; i++) 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 void
DmpAlg::showFvec() DmpAlg::showFvec(const Eigen::Vector3d &fvec)
{ {
for (int i = 0; i < nr_order_; i++) 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 void
DmpAlg::showJacobian() DmpAlg::showJacobian(const Eigen::Matrix3d &fjac)
{ {
std::string line = " "; std::string line = " ";
for (int j = 0; j < nr_order_; j++) for (int j = 0; j < nr_order_; j++)
@ -265,7 +269,7 @@ DmpAlg::showJacobian()
line.clear(); line.clear();
line += sta::format("{:4} ", dmp_func_index_strings[i]); line += sta::format("{:4} ", dmp_func_index_strings[i]);
for (int j = 0; j < nr_order_; j++) 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); report_->reportLine(line);
} }
} }
@ -505,7 +509,9 @@ DmpCap::loadDelaySlew(const Pin *,
} }
void void
DmpCap::evalDmpEqns() DmpCap::evalDmpEqns(Eigen::Vector3d &,
Eigen::Vector3d &,
Eigen::Matrix3d &)
{ {
} }
@ -584,7 +590,6 @@ DmpPi::gateDelaySlew()
double slew = 0.0; double slew = 0.0;
try { try {
findDriverParamsPi(); findDriverParamsPi();
ceff_ = x_[DmpParam::ceff];
auto [table_delay, table_slew] = gateCapDelaySlew(ceff_); auto [table_delay, table_slew] = gateCapDelaySlew(ceff_);
delay = table_delay; delay = table_delay;
// slew = table_slew; // slew = table_slew;
@ -623,60 +628,85 @@ DmpPi::findDriverParamsPi()
// Given x_ as a vector of input parameters, fill fvec_ with the // Given x_ as a vector of input parameters, fill fvec_ with the
// equations evaluated at x_ and fjac_ with the jacobian evaluated at x_. // equations evaluated at x_ and fjac_ with the jacobian evaluated at x_.
void void
DmpPi::evalDmpEqns() DmpPi::evalDmpEqns(Eigen::Vector3d &x,
Eigen::Vector3d &fvec,
Eigen::Matrix3d &fjac)
{ {
double t0 = x_[DmpParam::t0]; const double t0 = x[DmpParam::t0];
double dt = x_[DmpParam::dt]; const double dt = x[DmpParam::dt];
double ceff = x_[DmpParam::ceff]; 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"); throw DmpError("eqn eval failed: ceff < 0");
if (ceff > (c1_ + c2_)) }
if (ceff > (c1_ + c2_)) {
throw DmpError("eqn eval failed: ceff > c2 + c1"); 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); auto [t_vth, t_vl, slew] = gateDelays(ceff);
if (slew == 0.0) if (slew == 0.0) {
throw DmpError("eqn eval failed: slew = 0"); throw DmpError("eqn eval failed: slew = 0");
}
double ceff_time = slew / (vh_ - vl_); // ceff_time is bounded by 1.4 * dt.
ceff_time = std::min(ceff_time, 1.4 * dt); const double ceff_time = std::min(slew / (vh_ - vl_), 1.4 * dt);
if (dt <= 0.0) // Pre-calculate exponential terms to avoid redundant calls to
throw DmpError("eqn eval failed: dt < 0"); // 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); // Evaluate function values (residuals).
double exp_p2_dt = exp2(-p2_ * dt); const double y50 = y(t_vth, t0, dt, ceff).first;
double exp_dt_rd_ceff = exp2(-dt / (rd_ * ceff)); const double y20 = y(t_vl, t0, dt, ceff).first;
double y50 = y(t_vth, t0, dt, ceff).first; fvec[DmpFunc::ipi] = ipiIceff(t0, dt, ceff_time, ceff);
// Match Vl. fvec[DmpFunc::y50] = y50 - vth_;
double y20 = y(t_vl, t0, dt, ceff).first; fvec[DmpFunc::y20] = y20 - vl_;
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);
std::tie(fjac_[DmpFunc::y20][DmpParam::t0], // Pre-calculate common sub-expressions for the Jacobian derivatives.
fjac_[DmpFunc::y20][DmpParam::dt], const double b_div_p1 = B_ / p1_;
fjac_[DmpFunc::y20][DmpParam::ceff]) = dy(t_vl, t0, dt, ceff); const double d_div_p2 = D_ / p2_;
const double rd_ceff = rd_ * ceff;
std::tie(fjac_[DmpFunc::y50][DmpParam::t0], // Row 1 (Ipi derivatives).
fjac_[DmpFunc::y50][DmpParam::dt], fjac(DmpFunc::ipi, DmpParam::t0) = 0.0;
fjac_[DmpFunc::y50][DmpParam::ceff]) = dy(t_vth, t0, dt, ceff);
// 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)) { if (debug_->check("dmp_ceff", 4)) {
showX(); showX(x);
showFvec(); showFvec(fvec);
showJacobian(); showJacobian(fjac);
report_->report("................."); report_->report(".................");
} }
} }
@ -741,35 +771,37 @@ DmpOnePole::DmpOnePole(StaState *sta) :
} }
void void
DmpOnePole::evalDmpEqns() DmpOnePole::evalDmpEqns(Eigen::Vector3d &x,
Eigen::Vector3d &fvec,
Eigen::Matrix3d &fjac)
{ {
double t0 = x_[DmpParam::t0]; double t0 = x[DmpParam::t0];
double dt = x_[DmpParam::dt]; double dt = x[DmpParam::dt];
auto [t_vth, t_vl, ignore1] = gateDelays(ceff_); auto [t_vth, t_vl, ignore1] = gateDelays(ceff_);
double ignore2; double ignore2;
if (dt <= 0.0) 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::y50] = y(t_vth, t0, dt, ceff_).first - vth_;
fvec_[DmpFunc::y20] = y(t_vl, t0, dt, ceff_).first - vl_; fvec[DmpFunc::y20] = y(t_vl, t0, dt, ceff_).first - vl_;
if (debug_->check("dmp_ceff", 4)) { if (debug_->check("dmp_ceff", 4)) {
showX(); showX(x);
showFvec(); showFvec(fvec);
} }
std::tie(fjac_[DmpFunc::y20][DmpParam::t0], std::tie(fjac(DmpFunc::y20, DmpParam::t0),
fjac_[DmpFunc::y20][DmpParam::dt], fjac(DmpFunc::y20, DmpParam::dt),
ignore2) = dy(t_vl, t0, dt, ceff_); ignore2) = dy(t_vl, t0, dt, ceff_);
std::tie(fjac_[DmpFunc::y50][DmpParam::t0], std::tie(fjac(DmpFunc::y50, DmpParam::t0),
fjac_[DmpFunc::y50][DmpParam::dt], fjac(DmpFunc::y50, DmpParam::dt),
ignore2) = dy(t_vth, t0, dt, ceff_); ignore2) = dy(t_vth, t0, dt, ceff_);
if (debug_->check("dmp_ceff", 4)) { if (debug_->check("dmp_ceff", 4)) {
showJacobian(); showJacobian(fjac);
report_->report("................."); 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%). // driver_param_tol_ is the scale that all changes in x must be under (1.0 = 100%).
// evalDmpEqns() fills fvec_ and fjac_. // evalDmpEqns() fills fvec_ and fjac_.
void void
DmpAlg::newtonRaphson() DmpAlg::newtonRaphson(Eigen::Vector3d &x)
{ {
for (int k = 0; k < newton_raphson_max_iter_; k++) { Eigen::Vector3d fvec = Eigen::Vector3d::Zero();
evalDmpEqns(); Eigen::Matrix3d fjac = Eigen::Matrix3d::Zero();
for (int i = 0; i < nr_order_; i++) Eigen::Vector3d p = Eigen::Vector3d::Zero();
// Right-hand side of linear equations.
p_[i] = -fvec_[i]; for (int k = 0; k < newton_raphson_max_iter_; k++) {
luDecomp(); evalDmpEqns(x, fvec, fjac);
luSolve();
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) { if (all_under_x_tol) {
evalDmpEqns();
return; return;
} }
} }
throw DmpError("Newton-Raphson max iterations exceeded"); throw DmpError("Newton-Raphson max iterations exceeded");
} }
// luDecomp, luSolve based on MatClass from C. R. Birchenhall, // Solves the linear system J * p = -f (Jacobian * step = -residuals) for the Newton step.
// 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.
// //
// Replaces fjac_[0..nr_order_-1][*] by the LU decomposition. // This implementation uses a "Determinant Guarded" solver:
// index_[0..nr_order_-1] is an output vector of the row permutations. // 1. Manually computes/checks the determinant of the Jacobian (safety guard).
void // 2. If the determinant is dangerously close to zero (< 1e-12), throws a DmpError.
DmpAlg::luDecomp() // 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. double det = fjac.determinant();
for (int i = 0; i < size; i++) { if (std::abs(det) < 1e-12) {
double big = 0.0; throw DmpError("Jacobian is singular (order 3)");
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];
} }
p = fjac.inverse() * -fvec;
return p;
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////

View File

@ -27,6 +27,9 @@
#include <optional> #include <optional>
#include <utility> #include <utility>
#include <Eigen/Core>
#include <Eigen/Dense>
#include "LibertyClass.hh" #include "LibertyClass.hh"
#include "LumpedCapDelayCalc.hh" #include "LumpedCapDelayCalc.hh"
@ -59,18 +62,21 @@ public:
double elmore); double elmore);
double ceff() { return ceff_; } double ceff() { return ceff_; }
// Given x_ as a vector of input parameters, fill fvec_ with the virtual void
// equations evaluated at x_ and fjac_ with the jabobian evaluated at x_. evalDmpEqns(Eigen::Vector3d &x,
virtual void evalDmpEqns() = 0; Eigen::Vector3d &fvec,
Eigen::Matrix3d &fjac) = 0;
// Output response to vs(t) ramp driving pi model load (vo, dvo_dt). // Output response to vs(t) ramp driving pi model load (vo, dvo_dt).
std::pair<double, double> Vo(double t); std::pair<double, double> Vo(double t);
// Load response to driver waveform (vl, dvl/dt). // Load response to driver waveform (vl, dvl/dt).
std::pair<double, double> Vl(double t); std::pair<double, double> Vl(double t);
protected: protected:
void luDecomp(); void newtonRaphson(Eigen::Vector3d &x);
void luSolve(); // Solves J * p = -f using a fast analytical solver with singularity checks.
void newtonRaphson(); // 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. // Find driver parameters t0, delta_t, Ceff.
void findDriverParams(double ceff); void findDriverParams(double ceff);
std::pair<double, double> gateCapDelaySlew(double ceff); std::pair<double, double> gateCapDelaySlew(double ceff);
@ -84,9 +90,9 @@ protected:
double cl); double cl);
double y0dcl(double t, double y0dcl(double t,
double cl); double cl);
void showX(); void showX(const Eigen::Vector3d &x);
void showFvec(); void showFvec(const Eigen::Vector3d &fvec);
void showJacobian(); void showJacobian(const Eigen::Matrix3d &fjac);
std::pair<double, double> findDriverDelaySlew(); std::pair<double, double> findDriverDelaySlew();
double findVoCrossing(double vth, double findVoCrossing(double vth,
double t_lower, double t_lower,
@ -148,12 +154,7 @@ protected:
static constexpr int max_nr_order_ = 3; 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. // Driver slew used to check load delay.
double drvr_slew_; double drvr_slew_;
@ -194,7 +195,10 @@ public:
std::pair<double, double> gateDelaySlew() override; std::pair<double, double> gateDelaySlew() override;
std::pair<double, double> loadDelaySlew(const Pin *, std::pair<double, double> loadDelaySlew(const Pin *,
double elmore) override; double elmore) override;
void evalDmpEqns() override; void
evalDmpEqns(Eigen::Vector3d &x,
Eigen::Vector3d &fvec,
Eigen::Matrix3d &fjac) override;
protected: protected:
double voCrossingUpperBound() override; double voCrossingUpperBound() override;
@ -219,7 +223,10 @@ public:
double rpi, double rpi,
double c1) override; double c1) override;
std::pair<double, double> gateDelaySlew() override; std::pair<double, double> gateDelaySlew() override;
void evalDmpEqns() override; void
evalDmpEqns(Eigen::Vector3d &x,
Eigen::Vector3d &fvec,
Eigen::Matrix3d &fjac) override;
protected: protected:
double voCrossingUpperBound() override; double voCrossingUpperBound() override;
@ -255,7 +262,10 @@ class DmpOnePole : public DmpAlg
{ {
public: public:
DmpOnePole(StaState *sta); DmpOnePole(StaState *sta);
void evalDmpEqns() override; void
evalDmpEqns(Eigen::Vector3d &x,
Eigen::Vector3d &fvec,
Eigen::Matrix3d &fjac) override;
protected: protected:
double voCrossingUpperBound() override; double voCrossingUpperBound() override;

View File

@ -107,8 +107,7 @@ DcalcPred::searchThru(Edge *edge,
|| edge->isDisabledLoop() || edge->isDisabledLoop()
|| sdc->isDisabledConstraint(edge) || sdc->isDisabledConstraint(edge)
|| sdc->isDisabledCondDefault(edge) || sdc->isDisabledCondDefault(edge)
|| (edge->isBidirectInstPath() || sta_->isDisabledBidirectInstPath(edge));
&& !variables->bidirectInstPathsEnabled()));
} }
bool bool
@ -249,7 +248,7 @@ GraphDelayCalc::delayInvalid(Vertex *vertex)
{ {
debugPrint(debug_, "delay_calc", 2, "delay invalid {}", debugPrint(debug_, "delay_calc", 2, "delay invalid {}",
vertex->to_string(this)); vertex->to_string(this));
if (graph_ && incremental_) { if (incremental_) {
invalid_delays_.insert(vertex); invalid_delays_.insert(vertex);
// Invalidate driver that triggers dcalc for multi-driver nets. // Invalidate driver that triggers dcalc for multi-driver nets.
MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); MultiDrvrNet *multi_drvr = multiDrvrNet(vertex);
@ -339,47 +338,45 @@ FindVertexDelays::visit(Vertex *vertex)
void void
GraphDelayCalc::findDelays(Level level) GraphDelayCalc::findDelays(Level level)
{ {
if (arc_delay_calc_) { Stats stats(debug_, report_);
Stats stats(debug_, report_); int dcalc_count = 0;
int dcalc_count = 0; debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level);
debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level); if (!delays_seeded_) {
if (!delays_seeded_) { iter_->clear();
iter_->clear(); seedRootSlews();
seedRootSlews(); delays_seeded_ = true;
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");
} }
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 void
GraphDelayCalc::seedInvalidDelays() GraphDelayCalc::seedInvalidDelays()
{ {
for (Vertex *vertex : invalid_delays_) for (Vertex *vertex : invalid_delays_)
iter_->enqueue(vertex); iter_->enqueue(vertex);
invalid_delays_.clear(); invalid_delays_.clear();
} }
@ -1011,13 +1008,13 @@ GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex,
if (search_pred_->searchFrom(from_vertex, mode) if (search_pred_->searchFrom(from_vertex, mode)
&& search_pred_->searchThru(edge, mode)) { && search_pred_->searchThru(edge, mode)) {
for (const MinMax *min_max : MinMax::range()) { for (const MinMax *min_max : MinMax::range()) {
for (const TimingArc *arc : arc_set->arcs()) { for (const TimingArc *arc : arc_set->arcs()) {
delay_changed |= findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, delay_changed |= findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc,
scene, min_max, arc_delay_calc, scene, min_max, arc_delay_calc,
load_pin_index_map); load_pin_index_map);
delay_exists[arc->toEdge()->asRiseFall()->index()] = true; delay_exists[arc->toEdge()->asRiseFall()->index()] = true;
} }
} }
} }
} }
if (delay_changed && observer_) { if (delay_changed && observer_) {

View File

@ -24,6 +24,11 @@
This file summarizes STA API changes for each release. 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 Release 3.1.0 2026/03/25
------------------------ ------------------------

View File

@ -22,13 +22,13 @@ Release 3.0.1 2026/03/12
------------------------ ------------------------
Statistical timing (SSTA) with Liberty LVF (Liberty Variation Format) Statistical timing (SSTA) with Liberty LVF (Liberty Variation Format)
models is now supported. Statistical timing uses a probaility models is now supported. Statistical timing uses a probability
distribution to represent a delay or slew ranther than a single distribution to represent a delay or slew rather than a single
number. number.
Normal and skew normal probability distributions are supported. Normal and skew normal probability distributions are supported.
SSTA is enabled with the sta_pocv_mode variaable. SSTA is enabled with the sta_pocv_mode variable.
set sta_pocv_mode scalar|normal|skew_normal set sta_pocv_mode scalar|normal|skew_normal
@ -43,10 +43,10 @@ set with the sta_pocv_quantile variable.
The default value is 3 standard deviations, or sigma. The default value is 3 standard deviations, or sigma.
Use the variance field with report_checks or report_check_types to see Use the variation field with report_checks or report_check_types to see
distribution parameters in timing reports. distribution parameters in timing reports.
A command file for analyzing a design with statisical timing with an A command file for analyzing a design with statistical timing with an
LVF library is shown below. LVF library is shown below.
read_liberty lvf_library.lib.gz read_liberty lvf_library.lib.gz
@ -55,7 +55,7 @@ link_design top
create_clock -period 50 clk create_clock -period 50 clk
set_input_delay -clock clk 1 {in1 in2} set_input_delay -clock clk 1 {in1 in2}
set sta_pocv_mode skew_normal 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) Startpoint: r2 (rising edge-triggered flip-flop clocked by clk)
Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) Endpoint: r3 (rising edge-triggered flip-flop clocked by clk)

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -32,6 +32,7 @@
#include "Mutex.hh" #include "Mutex.hh"
#include "Network.hh" #include "Network.hh"
#include "PortDirection.hh" #include "PortDirection.hh"
#include "SearchPred.hh"
#include "Stats.hh" #include "Stats.hh"
#include "TimingArc.hh" #include "TimingArc.hh"
#include "TimingRole.hh" #include "TimingRole.hh"
@ -49,9 +50,9 @@ namespace sta {
Graph::Graph(StaState *sta, Graph::Graph(StaState *sta,
DcalcAPIndex ap_count) : DcalcAPIndex ap_count) :
StaState(sta), StaState(sta),
ap_count_(ap_count),
period_check_annotations_(network_), 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_. // For the benifit of reg_clk_vertices_ that references graph_.
graph_ = this; graph_ = this;
@ -250,6 +251,13 @@ Graph::makeInstDrvrWireEdges(const Instance *inst,
if (network_->isDriver(pin) if (network_->isDriver(pin)
&& !visited_drvrs.contains(pin)) && !visited_drvrs.contains(pin))
makeWireEdgesFromPin(pin, visited_drvrs); makeWireEdgesFromPin(pin, visited_drvrs);
if (network_->isTopInstance(inst)
&& network_->direction(pin)->isBidirect()) {
Vertex *bidir_load, *bidir_drvr;
pinVertices(pin, bidir_load, bidir_drvr);
Edge *edge = makeEdge(bidir_load, bidir_drvr, TimingArcSet::wireTimingArcSet());
edge->setIsBidirectPortPath(true);
}
} }
delete pin_iter; delete pin_iter;
} }
@ -384,13 +392,6 @@ Graph::makeWireEdge(const Pin *from_pin,
} }
} }
void
Graph::makeSceneAfter()
{
ap_count_ = dcalcAnalysisPtCount();
initSlews();
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
Vertex * Vertex *
@ -487,7 +488,7 @@ Graph::deleteVertex(Vertex *vertex)
EdgeId edge_id, next_id; EdgeId edge_id, next_id;
for (edge_id = vertex->in_edges_; edge_id; edge_id = next_id) { for (edge_id = vertex->in_edges_; edge_id; edge_id = next_id) {
Edge *edge = Graph::edge(edge_id); Edge *edge = Graph::edge(edge_id);
next_id = edge->vertex_in_link_; next_id = edge->vertex_in_next_;
deleteOutEdge(edge->from(this), edge); deleteOutEdge(edge->from(this), edge);
edge->clear(); edge->clear();
edges_->destroy(edge); edges_->destroy(edge);
@ -508,7 +509,7 @@ bool
Graph::hasFaninOne(Vertex *vertex) const Graph::hasFaninOne(Vertex *vertex) const
{ {
return vertex->in_edges_ return vertex->in_edges_
&& edge(vertex->in_edges_)->vertex_in_link_ == 0; && edge(vertex->in_edges_)->vertex_in_next_ == 0;
} }
void void
@ -519,12 +520,12 @@ Graph::deleteInEdge(Vertex *vertex,
EdgeId prev = 0; EdgeId prev = 0;
for (EdgeId i = vertex->in_edges_; for (EdgeId i = vertex->in_edges_;
i && i != edge_id; i && i != edge_id;
i = Graph::edge(i)->vertex_in_link_) i = Graph::edge(i)->vertex_in_next_)
prev = i; prev = i;
if (prev) if (prev)
Graph::edge(prev)->vertex_in_link_ = edge->vertex_in_link_; Graph::edge(prev)->vertex_in_next_ = edge->vertex_in_next_;
else else
vertex->in_edges_ = edge->vertex_in_link_; vertex->in_edges_ = edge->vertex_in_next_;
} }
void 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 Slew
Graph::slew(const Vertex *vertex, Graph::slew(const Vertex *vertex,
const RiseFall *rf, const RiseFall *rf,
@ -650,7 +721,7 @@ Graph::makeEdge(Vertex *from,
from->out_edges_ = edge_id; from->out_edges_ = edge_id;
// Add in edge to to vertex. // 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; to->in_edges_ = edge_id;
initArcDelays(edge); initArcDelays(edge);
@ -782,17 +853,13 @@ Graph::removeDelayAnnotated(Edge *edge)
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// This only gets called if the analysis type changes from single
// to bc_wc/ocv or visa versa.
void void
Graph::setDelayCount(DcalcAPIndex ap_count) Graph::delayCountChanged()
{ {
if (ap_count != ap_count_) { ap_count_ = dcalcAnalysisPtCount();
// Discard any existing delays. // Discard any existing delays.
removePeriodCheckAnnotations(); removePeriodCheckAnnotations();
ap_count_ = ap_count; initSlews();
initSlews();
}
} }
void void
@ -973,22 +1040,22 @@ Vertex::init(Pin *pin,
bool is_reg_clk) bool is_reg_clk)
{ {
pin_ = pin; pin_ = pin;
is_reg_clk_ = is_reg_clk;
is_bidirect_drvr_ = is_bidirect_drvr;
in_edges_ = edge_id_null; in_edges_ = edge_id_null;
out_edges_ = edge_id_null; out_edges_ = edge_id_null;
slews_ = nullptr; slews_ = nullptr;
paths_ = nullptr; paths_ = nullptr;
tag_group_index_ = tag_group_index_max; 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; has_checks_ = false;
is_check_clk_ = false; is_check_clk_ = false;
has_downstream_clk_pin_ = false; has_downstream_clk_pin_ = false;
level_ = 0;
visited1_ = false; visited1_ = false;
visited2_ = false; visited2_ = false;
has_sim_value_ = false; has_sim_value_ = false;
bfs_in_queue_ = 0; level_ = 0;
slew_annotated_ = false;
} }
Vertex::~Vertex() Vertex::~Vertex()
@ -1214,19 +1281,19 @@ Edge::init(VertexId from,
VertexId to, VertexId to,
TimingArcSet *arc_set) TimingArcSet *arc_set)
{ {
from_ = from;
to_ = to;
arc_set_ = arc_set; 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;
arc_delays_ = nullptr; arc_delays_ = nullptr;
arc_delay_annotated_is_bits_ = true; arc_delay_annotated_is_bits_ = true;
arc_delay_annotated_.bits_ = 0; 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; delay_annotation_is_incremental_ = false;
is_bidirect_inst_path_ = false;
is_bidirect_net_path_ = false;
is_bidirect_port_path_ = false;
is_disabled_loop_ = false; is_disabled_loop_ = false;
has_sim_sense_ = false; has_sim_sense_ = false;
has_disabled_cond_ = false; has_disabled_cond_ = false;
@ -1379,6 +1446,12 @@ Edge::setIsBidirectNetPath(bool is_bidir)
is_bidirect_net_path_ = is_bidir; is_bidirect_net_path_ = is_bidir;
} }
void
Edge::setIsBidirectPortPath(bool is_bidir)
{
is_bidirect_port_path_ = is_bidir;
}
void void
Edge::setHasSimSense(bool has_sense) Edge::setHasSimSense(bool has_sense)
{ {
@ -1461,6 +1534,8 @@ VertexIterator::findNext()
findNextPin(); findNextPin();
} }
////////////////////////////////////////////////////////////////
VertexInEdgeIterator::VertexInEdgeIterator(Vertex *vertex, VertexInEdgeIterator::VertexInEdgeIterator(Vertex *vertex,
const Graph *graph) : const Graph *graph) :
next_(graph->edge(vertex->in_edges_)), next_(graph->edge(vertex->in_edges_)),
@ -1480,7 +1555,7 @@ VertexInEdgeIterator::next()
{ {
Edge *next = next_; Edge *next = next_;
if (next_) if (next_)
next_ = graph_->edge(next_->vertex_in_link_); next_ = graph_->edge(next_->vertex_in_next_);
return next; return next;
} }

View File

@ -128,6 +128,7 @@ remove_delay_slew_annotations()
Pin *pin() { return self->pin(); } Pin *pin() { return self->pin(); }
bool is_bidirect_driver() { return self->isBidirectDriver(); } bool is_bidirect_driver() { return self->isBidirectDriver(); }
int level() { return Sta::sta()->vertexLevel(self); } int level() { return Sta::sta()->vertexLevel(self); }
bool is_root() { return self->isRoot(); }
int tag_group_index() { return self->tagGroupIndex(); } int tag_group_index() { return self->tagGroupIndex(); }
float float

View File

@ -81,12 +81,12 @@ protected:
void removeCell(ConcreteCell *cell); void removeCell(ConcreteCell *cell);
std::string name_; std::string name_;
ObjectId id_;
std::string filename_; std::string filename_;
ConcreteCellMap cell_map_;
ObjectId id_;
bool is_liberty_; bool is_liberty_;
char bus_brkt_left_{'['}; char bus_brkt_left_{'['};
char bus_brkt_right_{']'}; char bus_brkt_right_{']'};
ConcreteCellMap cell_map_;
private: private:
friend class ConcreteCell; friend class ConcreteCell;

View File

@ -366,10 +366,10 @@ protected:
ConcretePort *port_; ConcretePort *port_;
ConcreteNet *net_; ConcreteNet *net_;
ConcreteTerm *term_{nullptr}; ConcreteTerm *term_{nullptr};
ObjectId id_;
// Doubly linked list of net pins. // Doubly linked list of net pins.
ConcretePin *net_next_{nullptr}; ConcretePin *net_next_{nullptr};
ConcretePin *net_prev_{nullptr}; ConcretePin *net_prev_{nullptr};
ObjectId id_;
VertexId vertex_id_{vertex_id_null}; VertexId vertex_id_{vertex_id_null};
private: private:

View File

@ -1,11 +1,14 @@
// Author Phillip Johnston // Original Author: Phillip Johnston
// Licensed under CC0 1.0 Universal // Licensed under CC0 1.0 Universal
// https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp // Original source: https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp
// https://embeddedartistry.com/blog/2017/2/1/dispatch-queues?rq=dispatch // Original article: https://embeddedartistry.com/blog/2017/2/1/dispatch-queues?rq=dispatch
//
// Modified for OpenSTA to use C++20 non-spinning DynamicLatch for synchronization.
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <cstddef>
#include <condition_variable> #include <condition_variable>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
@ -16,6 +19,49 @@
namespace sta { namespace sta {
class DynamicLatch
{
public:
explicit DynamicLatch(std::ptrdiff_t initial_count = 0) :
count_(initial_count)
{
}
// Delete copy/move constructors to prevent accidental slicing/copying of atomics
DynamicLatch(const DynamicLatch&) = delete;
DynamicLatch& operator=(const DynamicLatch&) = delete;
// Increases the latch count (used when a new task is dispatched)
void
countUp()
{
count_.fetch_add(1, std::memory_order_release);
}
// Decreases the latch count and wakes waiting threads if it hits zero
void
countDown(std::ptrdiff_t n = 1)
{
if (count_.fetch_sub(n, std::memory_order_release) == n) {
count_.notify_all();
}
}
// Blocks until the count reaches zero
void
wait() const
{
std::ptrdiff_t current = count_.load(std::memory_order_acquire);
while (current != 0) {
count_.wait(current, std::memory_order_acquire);
current = count_.load(std::memory_order_acquire);
}
}
private:
mutable std::atomic<std::ptrdiff_t> count_{0};
};
class DispatchQueue class DispatchQueue
{ {
using fp_t = std::function<void(int thread)>; using fp_t = std::function<void(int thread)>;
@ -45,7 +91,7 @@ private:
std::vector<std::thread> threads_; std::vector<std::thread> threads_;
std::queue<fp_t> q_; std::queue<fp_t> q_;
std::condition_variable cv_; std::condition_variable cv_;
std::atomic<size_t> pending_task_count_; DynamicLatch pending_task_count_latch_;
bool quit_ = false; bool quit_ = false;
}; };

View File

@ -63,8 +63,7 @@ public:
void makeGraph(); void makeGraph();
~Graph() override; ~Graph() override;
// Number of arc delays and slews from sdf or delay calculation. void delayCountChanged();
void setDelayCount(DcalcAPIndex ap_count);
size_t slewCount(); size_t slewCount();
// Vertex functions. // Vertex functions.
@ -88,6 +87,19 @@ public:
bool hasFaninOne(Vertex *vertex) const; bool hasFaninOne(Vertex *vertex) const;
VertexId vertexCount() { return vertices_->size(); } 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 slew are the same as those in the liberty tables.
// reported_slews = measured_slews / slew_derate_from_library // reported_slews = measured_slews / slew_derate_from_library
// Measured slews are between slew_lower_threshold and slew_upper_threshold. // Measured slews are between slew_lower_threshold and slew_upper_threshold.
@ -178,10 +190,9 @@ public:
// Remove all delay and slew annotations. // Remove all delay and slew annotations.
void removeDelaySlewAnnotations(); void removeDelaySlewAnnotations();
VertexSet &regClkVertices() { return reg_clk_vertices_; } VertexSet &regClkVertices() { return reg_clk_vertices_; }
void makeSceneAfter();
static constexpr int vertex_level_bits = 24; 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: protected:
void makeVerticesAndEdges(); void makeVerticesAndEdges();
@ -218,11 +229,11 @@ protected:
// driver/source (top level input, instance pin output) vertex // driver/source (top level input, instance pin output) vertex
// in pin_bidirect_drvr_vertex_map // in pin_bidirect_drvr_vertex_map
PinVertexMap pin_bidirect_drvr_vertex_map_; PinVertexMap pin_bidirect_drvr_vertex_map_;
DcalcAPIndex ap_count_;
// Sdf period check annotations. // Sdf period check annotations.
PeriodCheckAnnotations period_check_annotations_; PeriodCheckAnnotations period_check_annotations_;
// Register/latch clock vertices to search from. // Register/latch clock vertices to search from.
VertexSet reg_clk_vertices_; VertexSet reg_clk_vertices_;
DcalcAPIndex ap_count_;
friend class Vertex; friend class Vertex;
friend class VertexIterator; friend class VertexIterator;
@ -313,21 +324,20 @@ protected:
// Each bit corresponds to a different BFS queue. // Each bit corresponds to a different BFS queue.
std::atomic<uint8_t> bfs_in_queue_; // 8 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. // Bidirect pins have two vertices.
// This flag distinguishes the driver and load vertices. // This flag distinguishes the driver and load vertices.
bool is_bidirect_drvr_:1; unsigned int is_bidirect_drvr_:1;
unsigned int is_reg_clk_:1;
bool is_reg_clk_:1;
// Constrained by timing check edge. // Constrained by timing check edge.
bool has_checks_:1; unsigned int has_checks_:1;
// Is the clock for a timing check. // Is the clock for a timing check.
bool is_check_clk_:1; unsigned int is_check_clk_:1;
bool has_downstream_clk_pin_:1; unsigned int has_downstream_clk_pin_:1;
bool visited1_:1; unsigned int visited1_:1;
bool visited2_:1; unsigned int visited2_:1;
bool has_sim_value_:1; unsigned int has_sim_value_:1;
int level_:Graph::vertex_level_bits; // 24
unsigned int slew_annotated_:slew_annotated_bits; // 4
private: private:
friend class Graph; friend class Graph;
@ -366,6 +376,9 @@ public:
void setIsBidirectInstPath(bool is_bidir); void setIsBidirectInstPath(bool is_bidir);
bool isBidirectNetPath() const { return is_bidirect_net_path_; } bool isBidirectNetPath() const { return is_bidirect_net_path_; }
void setIsBidirectNetPath(bool is_bidir); void setIsBidirectNetPath(bool is_bidir);
bool isBidirectPortPath() const { return is_bidirect_port_path_; }
void setIsBidirectPortPath(bool is_bidir);
void removeDelayAnnotated(); void removeDelayAnnotated();
[[nodiscard]] bool hasSimSense() const { return has_sim_sense_; } [[nodiscard]] bool hasSimSense() const { return has_sim_sense_; }
void setHasSimSense(bool has_sense); void setHasSimSense(bool has_sense);
@ -391,20 +404,22 @@ protected:
static uintptr_t arcDelayAnnotateBit(size_t index); static uintptr_t arcDelayAnnotateBit(size_t index);
TimingArcSet *arc_set_; 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_; float *arc_delays_;
union { union {
uintptr_t bits_; uintptr_t bits_;
std::vector<bool> *seq_; std::vector<bool> *seq_;
} arc_delay_annotated_; } 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 arc_delay_annotated_is_bits_:1;
bool delay_annotation_is_incremental_:1; bool delay_annotation_is_incremental_:1;
bool is_bidirect_inst_path_:1; bool is_bidirect_inst_path_:1;
bool is_bidirect_net_path_:1; bool is_bidirect_net_path_:1;
// Bidirect load -> driver edge.
bool is_bidirect_port_path_:1;
bool is_disabled_loop_:1; bool is_disabled_loop_:1;
bool has_sim_sense_:1; bool has_sim_sense_:1;
bool has_disabled_cond_:1; bool has_disabled_cond_:1;

View File

@ -65,6 +65,8 @@ using Level = int;
using DcalcAPIndex = int; using DcalcAPIndex = int;
using TagGroupIndex = int; using TagGroupIndex = int;
using SlewSeq = std::vector<Slew>; 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(); static constexpr int level_max = std::numeric_limits<Level>::max();

View File

@ -25,7 +25,6 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <string_view> #include <string_view>
namespace sta { namespace sta {
@ -59,12 +58,4 @@ nextMersenne(size_t n)
size_t size_t
hashString(std::string_view str); hashString(std::string_view str);
// Pointer hashing is strongly discouraged because it causes results to change
// from run to run. Use Network::id functions instead.
#if __WORDSIZE == 64
#define hashPtr(ptr) (reinterpret_cast<intptr_t>(ptr) >> 3)
#else
#define hashPtr(ptr) (reinterpret_cast<intptr_t>(ptr) >> 2)
#endif
} // namespace sta } // namespace sta

View File

@ -536,7 +536,9 @@ public:
bool leakagePowerExists() const { return leakage_power_exists_; } bool leakagePowerExists() const { return leakage_power_exists_; }
// Register, Latch or Statetable. // 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_; } const SequentialSeq &sequentials() const { return sequentials_; }
// Find the sequential with the output connected to an (internal) port. // Find the sequential with the output connected to an (internal) port.
Sequential *outputPortSequential(LibertyPort *port); Sequential *outputPortSequential(LibertyPort *port);
@ -861,6 +863,8 @@ public:
void setIsRegOutput(bool is_reg_out); void setIsRegOutput(bool is_reg_out);
bool isLatchData() const { return is_latch_data_; } bool isLatchData() const { return is_latch_data_; }
void setIsLatchData(bool 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. // Is the clock for timing checks.
bool isCheckClk() const { return is_check_clk_; } bool isCheckClk() const { return is_check_clk_; }
void setIsCheckClk(bool is_clk); void setIsCheckClk(bool is_clk);
@ -956,6 +960,7 @@ protected:
bool is_reg_clk_:1 {false}; bool is_reg_clk_:1 {false};
bool is_reg_output_:1 {false}; bool is_reg_output_:1 {false};
bool is_latch_data_: 1 {false}; bool is_latch_data_: 1 {false};
bool is_latch_output_:1 {false};
bool is_check_clk_:1 {false}; bool is_check_clk_:1 {false};
bool is_clk_gate_clk_:1 {false}; bool is_clk_gate_clk_:1 {false};
bool is_clk_gate_enable_:1 {false}; bool is_clk_gate_enable_:1 {false};

View File

@ -319,6 +319,7 @@ public:
// Pin clocks a timing check. // Pin clocks a timing check.
[[nodiscard]] bool isCheckClk(const Pin *pin) const; [[nodiscard]] bool isCheckClk(const Pin *pin) const;
[[nodiscard]] bool isLatchData(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 // Iterate over all of the pins connected to a pin and the parent
// and child nets it is hierarchically connected to (port, leaf and // and child nets it is hierarchically connected to (port, leaf and
@ -502,6 +503,8 @@ class NetworkEdit : public Network
public: public:
NetworkEdit() = default; NetworkEdit() = default;
bool isEditable() const override { return true; } bool isEditable() const override { return true; }
virtual Port *makePort(Cell *cell,
std::string_view name) = 0;
virtual Instance *makeInstance(LibertyCell *cell, virtual Instance *makeInstance(LibertyCell *cell,
std::string_view name, std::string_view name,
Instance *parent) = 0; Instance *parent) = 0;
@ -556,8 +559,6 @@ public:
virtual void setAttribute(Instance *instance, virtual void setAttribute(Instance *instance,
std::string_view key, std::string_view key,
std::string_view value) = 0; std::string_view value) = 0;
virtual Port *makePort(Cell *cell,
std::string_view name) = 0;
virtual Port *makeBusPort(Cell *cell, virtual Port *makeBusPort(Cell *cell,
std::string_view name, std::string_view name,
int from_index, int from_index,

View File

@ -94,11 +94,12 @@ parseBusName(std::string_view name,
int &to, int &to,
bool &subscript_wild); bool &subscript_wild);
// Insert escapes before ch1 and ch2 in token. // Insert escapes before ch1, ch2 or ch3 in token.
std::string std::string
escapeChars(std::string_view token, escapeChars(std::string_view token,
char ch1, char ch1,
char ch2, char ch2,
char ch3,
char escape); char escape);
} // namespace sta } // namespace sta

View File

@ -146,8 +146,9 @@ public:
PathGroup *findPathGroup(const Clock *clock, PathGroup *findPathGroup(const Clock *clock,
const MinMax *min_max) const; const MinMax *min_max) const;
PathGroupSeq pathGroups(const PathEnd *path_end) const; PathGroupSeq pathGroups(const PathEnd *path_end) const;
static StringSeq pathGroupNames(const PathEnd *path_end, static bool inPathGroupNamed(const PathEnd *path_end,
const StaState *sta); std::string_view path_group_name,
const StaState *sta);
static std::string_view asyncPathGroupName() { return async_group_name_; } static std::string_view asyncPathGroupName() { return async_group_name_; }
static std::string_view pathDelayGroupName() { return path_delay_group_name_; } static std::string_view pathDelayGroupName() { return path_delay_group_name_; }
static std::string_view gatedClkGroupName() { return gated_clk_group_name_; } static std::string_view gatedClkGroupName() { return gated_clk_group_name_; }

View File

@ -51,6 +51,8 @@ public:
const Pin *refPin() const { return ref_pin_; } const Pin *refPin() const { return ref_pin_; }
void setRefPin(const Pin *ref_pin); void setRefPin(const Pin *ref_pin);
const RiseFall *refTransition() const; const RiseFall *refTransition() const;
bool refPinEdgesExist() const { return ref_pin_edges_exist_; }
void setRefPinEdgesExist(bool exists);
protected: protected:
PortDelay(const Pin *pin, PortDelay(const Pin *pin,
@ -62,6 +64,7 @@ protected:
bool source_latency_included_{false}; bool source_latency_included_{false};
bool network_latency_included_{false}; bool network_latency_included_{false};
const Pin *ref_pin_{nullptr}; const Pin *ref_pin_{nullptr};
bool ref_pin_edges_exist_{false};
RiseFallMinMax delays_; RiseFallMinMax delays_;
PinSet leaf_pins_; PinSet leaf_pins_;
}; };

View File

@ -42,6 +42,8 @@ class PropertyValue;
class Sta; class Sta;
class PropertyValue; class PropertyValue;
class Scene;
class Mode;
template<class TYPE> template<class TYPE>
class PropertyRegistry class PropertyRegistry
@ -86,6 +88,10 @@ public:
std::string_view property); std::string_view property);
PropertyValue getProperty(const Clock *clk, PropertyValue getProperty(const Clock *clk,
std::string_view property); std::string_view property);
PropertyValue getProperty(const Scene *scene,
std::string_view property);
PropertyValue getProperty(const Mode *mode,
std::string_view property);
PropertyValue getProperty(PathEnd *end, PropertyValue getProperty(PathEnd *end,
std::string_view property); std::string_view property);
PropertyValue getProperty(Path *path, PropertyValue getProperty(Path *path,

View File

@ -576,6 +576,8 @@ public:
const Clock *clk, const Clock *clk,
const RiseFall *clk_rf, const RiseFall *clk_rf,
const MinMaxAll *min_max); const MinMaxAll *min_max);
void ensureInputDelayRefPinEdges();
void inputDelayRefPinEdgesInvalid();
void setOutputDelay(const Pin *pin, void setOutputDelay(const Pin *pin,
const RiseFallBoth *rf, const RiseFallBoth *rf,
const Clock *clk, const Clock *clk,
@ -806,7 +808,6 @@ public:
const MinMaxAll *min_max); const MinMaxAll *min_max);
// STA interface. // STA interface.
InputDelaySet *refPinInputDelays(const Pin *ref_pin) const;
const LogicValueMap &logicValues() const { return logic_value_map_; } const LogicValueMap &logicValues() const { return logic_value_map_; }
const LogicValueMap &caseLogicValues() const { return case_value_map_; } const LogicValueMap &caseLogicValues() const { return case_value_map_; }
// Returns nullptr if set_operating_conditions has not been called. // Returns nullptr if set_operating_conditions has not been called.
@ -1221,6 +1222,7 @@ protected:
InputDelay *except); InputDelay *except);
void deleteInputDelaysReferencing(const Clock *clk); void deleteInputDelaysReferencing(const Clock *clk);
void deleteInputDelay(InputDelay *input_delay); void deleteInputDelay(InputDelay *input_delay);
OutputDelay *findOutputDelay(const Pin *pin, OutputDelay *findOutputDelay(const Pin *pin,
const ClockEdge *clk_edge); const ClockEdge *clk_edge);
OutputDelay *makeOutputDelay(const Pin *pin, OutputDelay *makeOutputDelay(const Pin *pin,
@ -1229,6 +1231,7 @@ protected:
OutputDelay *except); OutputDelay *except);
void deleteOutputDelaysReferencing(const Clock *clk); void deleteOutputDelaysReferencing(const Clock *clk);
void deleteOutputDelay(OutputDelay *output_delay); void deleteOutputDelay(OutputDelay *output_delay);
void deleteClockInsertion(ClockInsertion *insertion); void deleteClockInsertion(ClockInsertion *insertion);
void deleteClockInsertionsReferencing(Clock *clk); void deleteClockInsertionsReferencing(Clock *clk);
void deleteInterClockUncertainty(InterClockUncertainty *uncertainties); void deleteInterClockUncertainty(InterClockUncertainty *uncertainties);
@ -1334,7 +1337,7 @@ protected:
InputDelaySet input_delays_; InputDelaySet input_delays_;
InputDelaysPinMap input_delay_pin_map_; InputDelaysPinMap input_delay_pin_map_;
InputDelaysPinMap input_delay_ref_pin_map_; bool have_input_delay_ref_pins_{false};
// Input delays on hierarchical pins are indexed by the load pins. // Input delays on hierarchical pins are indexed by the load pins.
InputDelaysPinMap input_delay_leaf_pin_map_; InputDelaysPinMap input_delay_leaf_pin_map_;
InputDelaysPinMap input_delay_internal_pin_map_; InputDelaysPinMap input_delay_internal_pin_map_;
@ -1342,7 +1345,6 @@ protected:
OutputDelaySet output_delays_; OutputDelaySet output_delays_;
OutputDelaysPinMap output_delay_pin_map_; OutputDelaysPinMap output_delay_pin_map_;
OutputDelaysPinMap output_delay_ref_pin_map_;
// Output delays on hierarchical pins are indexed by the load pins. // Output delays on hierarchical pins are indexed by the load pins.
OutputDelaysPinMap output_delay_leaf_pin_map_; OutputDelaysPinMap output_delay_leaf_pin_map_;

View File

@ -92,6 +92,8 @@ public:
int index) const override; int index) const override;
PortMemberIterator *memberIterator(const Port *port) const override; PortMemberIterator *memberIterator(const Port *port) const override;
bool hasMembers(const Port *port) const override; bool hasMembers(const Port *port) const override;
Port *makePort(Cell *cell,
std::string_view name) override;
ObjectId id(const Instance *instance) const override; ObjectId id(const Instance *instance) const override;
std::string getAttribute(const Instance *inst, std::string getAttribute(const Instance *inst,
@ -146,6 +148,7 @@ public:
char pathEscape() const override; char pathEscape() const override;
void setPathEscape(char escape) override; void setPathEscape(char escape) override;
// NetworkEdit
bool isEditable() const override; bool isEditable() const override;
LibertyLibrary *makeLibertyLibrary(std::string_view name, LibertyLibrary *makeLibertyLibrary(std::string_view name,
std::string_view filename) override; std::string_view filename) override;
@ -202,6 +205,8 @@ public:
const PatternMatch *pattern) const override; const PatternMatch *pattern) const override;
std::string name(const Port *port) const override; std::string name(const Port *port) const override;
std::string busName(const Port *port) const override; std::string busName(const Port *port) const override;
Port *makePort(Cell *cell,
std::string_view name) override;
std::string name(const Instance *instance) const override; std::string name(const Instance *instance) const override;
std::string pathName(const Instance *instance) const override; std::string pathName(const Instance *instance) const override;
@ -289,5 +294,8 @@ escapeDividers(std::string_view name,
std::string std::string
escapeBrackets(std::string_view name, escapeBrackets(std::string_view name,
const Network *network); const Network *network);
std::string
escapeDividerBrackets(std::string_view name,
const Network *network);
} // namespace sta } // namespace sta

View File

@ -284,8 +284,8 @@ public:
Vertex *vertex, Vertex *vertex,
const Mode *mode, const Mode *mode,
TagGroupBldr *tag_bldr); TagGroupBldr *tag_bldr);
void enqueueLatchDataOutputs(Vertex *vertex); void postponeLatchDataOutputs(Vertex *vertex);
void enqueueLatchOutput(Vertex *vertex); void postponeArrivals(Vertex *vertex);
void enqueuePendingClkFanouts(); void enqueuePendingClkFanouts();
void postponeClkFanouts(Vertex *vertex); void postponeClkFanouts(Vertex *vertex);
void seedRequired(Vertex *vertex); void seedRequired(Vertex *vertex);
@ -353,7 +353,8 @@ public:
TagGroup *tagGroup(const Vertex *vertex) const; TagGroup *tagGroup(const Vertex *vertex) const;
TagGroup *tagGroup(TagGroupIndex index) const; TagGroup *tagGroup(TagGroupIndex index) const;
void reportArrivals(Vertex *vertex, void reportArrivals(Vertex *vertex,
bool report_tag_index) const; bool report_tag_index,
int digits) const;
Slack wnsSlack(Vertex *vertex, Slack wnsSlack(Vertex *vertex,
PathAPIndex path_ap_index); PathAPIndex path_ap_index);
void levelsChangedBefore(); void levelsChangedBefore();
@ -405,7 +406,6 @@ public:
void checkPrevPaths() const; void checkPrevPaths() const;
void deletePaths(Vertex *vertex); void deletePaths(Vertex *vertex);
void deleteTagGroup(TagGroup *group); void deleteTagGroup(TagGroup *group);
bool postponeLatchOutputs() const { return postpone_latch_outputs_; }
void saveEnumPath(Path *path); void saveEnumPath(Path *path);
bool isSrchRoot(Vertex *vertex, bool isSrchRoot(Vertex *vertex,
const Mode *mode) const; const Mode *mode) const;
@ -529,9 +529,6 @@ protected:
const MinMax *min_max) const; const MinMax *min_max) const;
void seedRequireds(); void seedRequireds();
void seedInvalidRequireds(); void seedInvalidRequireds();
[[nodiscard]] bool havePendingLatchOutputs();
void clearPendingLatchOutputs();
void enqueuePendingLatchOutputs();
void findFilteredArrivals(bool thru_latches); void findFilteredArrivals(bool thru_latches);
void findArrivalsSeed(); void findArrivalsSeed();
void seedFilterStarts(); void seedFilterStarts();
@ -593,7 +590,7 @@ protected:
// Search predicates. // Search predicates.
SearchPred *search_thru_; SearchPred *search_thru_;
SearchAdj *search_adj_; SearchPred *search_adj_;
EvalPred *eval_pred_; EvalPred *eval_pred_;
// Some arrivals exist. // Some arrivals exist.
@ -650,11 +647,11 @@ protected:
std::mutex tag_group_lock_; std::mutex tag_group_lock_;
// Latches data outputs to queue on the next search pass. // Latches data outputs to queue on the next search pass.
VertexSet pending_latch_outputs_; VertexSet postponed_arrivals_;
std::mutex pending_latch_outputs_lock_; std::mutex postponed_arrivals_lock_;
// Clock network endpoints where arrival search was suppended by findClkArrivals(). // Clock network endpoints where arrival search was suppended by findClkArrivals().
VertexSet pending_clk_endpoints_; VertexSet postponed_clk_endpoints_;
std::mutex pending_clk_endpoints_lock_; std::mutex postponed_clk_endpoints_lock_;
VertexSet endpoints_; VertexSet endpoints_;
bool endpoints_initialized_{false}; bool endpoints_initialized_{false};
@ -668,7 +665,6 @@ protected:
std::mutex filtered_arrivals_lock_; std::mutex filtered_arrivals_lock_;
bool found_downstream_clk_pins_{false}; bool found_downstream_clk_pins_{false};
bool postpone_latch_outputs_{false};
std::vector<Path*> enum_paths_; std::vector<Path*> enum_paths_;
VisitPathEnds *visit_path_ends_; VisitPathEnds *visit_path_ends_;
@ -711,7 +707,8 @@ public:
bool make_tag_cache, bool make_tag_cache,
const StaState *sta); const StaState *sta);
~PathVisitor() override; ~PathVisitor() override;
virtual void visitFaninPaths(Vertex *to_vertex); virtual void visitFaninPaths(Vertex *to_vertex,
bool with_latch_edges);
virtual void visitFanoutPaths(Vertex *from_vertex); virtual void visitFanoutPaths(Vertex *from_vertex);
// Return false to stop visiting. // Return false to stop visiting.
virtual bool visitFromToPath(const Pin *from_pin, virtual bool visitFromToPath(const Pin *from_pin,
@ -778,6 +775,8 @@ public:
SearchPred *pred); SearchPred *pred);
void copyState(const StaState *sta) override; void copyState(const StaState *sta) override;
void visit(Vertex *vertex) override; void visit(Vertex *vertex) override;
void visit(Vertex *vertex,
bool with_latch_edges);
VertexVisitor *copy() const override; VertexVisitor *copy() const override;
// Return false to stop visiting. // Return false to stop visiting.
bool visitFromToPath(const Pin *from_pin, bool visitFromToPath(const Pin *from_pin,
@ -799,18 +798,18 @@ public:
protected: protected:
void init0(); void init0();
void enqueueRefPinInputDelays(const Pin *ref_pin,
const Sdc *sdc);
void seedArrivals(Vertex *vertex); void seedArrivals(Vertex *vertex);
void pruneCrprArrivals(); void pruneCrprArrivals();
void constrainedRequiredsInvalid(Vertex *vertex, void constrainedRequiredsInvalid(Vertex *vertex,
bool is_clk); bool is_clk);
bool hasPendingLoopPaths(Edge *edge) const;
bool always_to_endpoints_; bool always_to_endpoints_;
bool always_save_prev_paths_; bool always_save_prev_paths_;
bool clks_only_; bool clks_only_;
TagGroupBldr *tag_bldr_; TagGroupBldr *tag_bldr_;
TagGroupBldr *tag_bldr_no_crpr_; TagGroupBldr *tag_bldr_no_crpr_;
SearchPred *adj_pred_; SearchPred *search_adj_;
bool crpr_active_; bool crpr_active_;
bool has_fanin_one_; bool has_fanin_one_;
}; };

View File

@ -51,19 +51,19 @@ public:
// Search is allowed from from_vertex. // Search is allowed from from_vertex.
virtual bool searchFrom(const Vertex *from_vertex, virtual bool searchFrom(const Vertex *from_vertex,
const Mode *mode) const = 0; 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. // Search is allowed through edge.
// from/to pins are NOT checked. // from/to pins are NOT checked.
// inst can be either the from_pin or to_pin instance because it // inst can be either the from_pin or to_pin instance because it
// is only referenced when they are the same (non-wire edge). // is only referenced when they are the same (non-wire edge).
virtual bool searchThru(Edge *edge, virtual bool searchThru(Edge *edge,
const Mode *mode) const = 0; const Mode *mode) const = 0;
bool searchThru(Edge *edge) const; virtual bool searchThru(Edge *edge) const;
// Search is allowed to to_pin. // Search is allowed to to_pin.
virtual bool searchTo(const Vertex *to_vertex, virtual bool searchTo(const Vertex *to_vertex,
const Mode *mode) const = 0; const Mode *mode) const = 0;
bool searchTo(const Vertex *to_vertex) const; virtual bool searchTo(const Vertex *to_vertex) const;
void copyState(const StaState *sta); virtual void copyState(const StaState *sta);
protected: protected:
const StaState *sta_; const StaState *sta_;

View File

@ -567,8 +567,6 @@ public:
const Sdc *sdc); const Sdc *sdc);
// Edge is disabled to break combinational loops. // Edge is disabled to break combinational loops.
[[nodiscard]] bool isDisabledLoop(Edge *edge) const; [[nodiscard]] bool isDisabledLoop(Edge *edge) const;
// Edge is disabled internal bidirect output path.
[[nodiscard]] bool isDisabledBidirectInstPath(Edge *edge) const;
// Edge is disabled bidirect net path. // Edge is disabled bidirect net path.
[[nodiscard]] bool isDisabledBidirectNetPath(Edge *edge) const; [[nodiscard]] bool isDisabledBidirectNetPath(Edge *edge) const;
[[nodiscard]] bool isDisabledPresetClr(Edge *edge) const; [[nodiscard]] bool isDisabledPresetClr(Edge *edge) const;
@ -899,6 +897,10 @@ public:
const MinMaxAll *min_max, const MinMaxAll *min_max,
const RiseFallBoth *rf, const RiseFallBoth *rf,
float slew); float slew);
void unsetAnnotatedSlew(Vertex *vertex,
const Scene *scene,
const MinMaxAll *min_max,
const RiseFallBoth *rf);
void writeSdf(std::string_view filename, void writeSdf(std::string_view filename,
const Scene *scene, const Scene *scene,
char divider, char divider,
@ -1040,6 +1042,7 @@ public:
int endpointViolationCount(const MinMax *min_max); int endpointViolationCount(const MinMax *min_max);
// Find all required times after updateTiming(). // Find all required times after updateTiming().
void findRequireds(); void findRequireds();
void findRequired(Vertex *vertex);
std::string reportDelayCalc(Edge *edge, std::string reportDelayCalc(Edge *edge,
TimingArc *arc, TimingArc *arc,
const Scene *scene, const Scene *scene,
@ -1528,7 +1531,6 @@ protected:
const Mode *mode, const Mode *mode,
// Return value. // Return value.
PinSet &pins); PinSet &pins);
void findRequired(Vertex *vertex);
void reportDelaysWrtClks(const Pin *pin, void reportDelaysWrtClks(const Pin *pin,
const Scene *scene, const Scene *scene,

View File

@ -106,6 +106,8 @@ public:
const Variables *variables() const { return variables_; } const Variables *variables() const { return variables_; }
// Edge is default cond disabled by timing_disable_cond_default_arcs var. // Edge is default cond disabled by timing_disable_cond_default_arcs var.
[[nodiscard]] bool isDisabledCondDefault(const Edge *edge) const; [[nodiscard]] bool isDisabledCondDefault(const Edge *edge) const;
// Edge is disabled internal bidirect output path.
[[nodiscard]] bool isDisabledBidirectInstPath(Edge *edge) const;
const SceneSeq &scenes() { return scenes_; } const SceneSeq &scenes() { return scenes_; }
const SceneSeq &scenes() const { return scenes_; } const SceneSeq &scenes() const { return scenes_; }

View File

@ -204,6 +204,9 @@ public:
static int wireArcIndex(const RiseFall *rf); static int wireArcIndex(const RiseFall *rf);
static int wireArcCount() { return 2; } static int wireArcCount() { return 2; }
// Psuedo definition for port ref_pin arcs.
static TimingArcSet *portRefPinTimingArcSet() { return port_refpin_timing_arc_set_; }
protected: protected:
TimingArcSet(const TimingRole *role, TimingArcSet(const TimingRole *role,
TimingArcAttrsPtr attrs); TimingArcAttrsPtr attrs);
@ -230,6 +233,7 @@ protected:
static TimingArcAttrsPtr wire_timing_arc_attrs_; static TimingArcAttrsPtr wire_timing_arc_attrs_;
static TimingArcSet *wire_timing_arc_set_; static TimingArcSet *wire_timing_arc_set_;
static TimingArcSet *port_refpin_timing_arc_set_;
}; };
// A timing arc is a single from/to transition between two ports. // A timing arc is a single from/to transition between two ports.

View File

@ -68,6 +68,7 @@ public:
static const TimingRole *nonSeqHold() { return &non_seq_hold_; } static const TimingRole *nonSeqHold() { return &non_seq_hold_; }
static const TimingRole *clockTreePathMin() { return &clock_tree_path_min_; } static const TimingRole *clockTreePathMin() { return &clock_tree_path_min_; }
static const TimingRole *clockTreePathMax() { return &clock_tree_path_max_; } static const TimingRole *clockTreePathMax() { return &clock_tree_path_max_; }
static const TimingRole *portDelayRefPin() { return &port_delay_ref_pin_; }
const std::string &to_string() const { return name_; } const std::string &to_string() const { return name_; }
int index() const { return index_; } int index() const { return index_; }
bool isWire() const; bool isWire() const;
@ -139,6 +140,8 @@ private:
static const TimingRole non_seq_hold_; static const TimingRole non_seq_hold_;
static const TimingRole clock_tree_path_min_; static const TimingRole clock_tree_path_min_;
static const TimingRole clock_tree_path_max_; static const TimingRole clock_tree_path_max_;
// Artificial timing from input/output_delay ref_pin to the input/output.
static const TimingRole port_delay_ref_pin_;
static TimingRoleMap timing_roles_; static TimingRoleMap timing_roles_;
friend class TimingRoleLess; friend class TimingRoleLess;

View File

@ -1438,6 +1438,12 @@ LibertyCell::outputPortSequential(LibertyPort *port)
return nullptr; return nullptr;
} }
bool
LibertyCell::isSequential() const
{
return !sequentials_.empty() || statetable_ != nullptr;
}
bool bool
LibertyCell::hasSequentials() const LibertyCell::hasSequentials() const
{ {
@ -1618,7 +1624,7 @@ void
LibertyCell::makeLatchEnables(Report *report, LibertyCell::makeLatchEnables(Report *report,
Debug *debug) Debug *debug)
{ {
if (hasSequentials() if (isSequential()
|| hasInferedRegTimingArcs()) { || hasInferedRegTimingArcs()) {
for (TimingArcSet *d_to_q : timing_arc_sets_) { for (TimingArcSet *d_to_q : timing_arc_sets_) {
if (d_to_q->role() == TimingRole::latchDtoQ()) { 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_d_to_q_map_[d_to_q] = idx;
latch_check_map_[setup_check] = idx; latch_check_map_[setup_check] = idx;
d->setIsLatchData(true); d->setIsLatchData(true);
q->setIsLatchOutput(true);
debugPrint(debug, "liberty_latch", 1, debugPrint(debug, "liberty_latch", 1,
"latch {} -> {} | {} {} -> {} | {} {} -> {} setup", "latch {} -> {} | {} {} -> {} | {} {} -> {} setup",
d->name(), d->name(),
@ -2434,6 +2441,13 @@ LibertyPort::setIsLatchData(bool is_latch_data)
setMemberFlag(is_latch_data, &LibertyPort::setIsLatchData); 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 void
LibertyPort::setIsCheckClk(bool is_clk) LibertyPort::setIsCheckClk(bool is_clk)
{ {

View File

@ -3081,7 +3081,8 @@ libertyReaderFindPort(const LibertyCell *cell,
char brkt_right = library->busBrktRight(); char brkt_right = library->busBrktRight();
const char escape = '\\'; const char escape = '\\';
// Pins at top level with bus names have escaped brackets. // Pins at top level with bus names have escaped brackets.
std::string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right, escape); std::string escaped_port_name = escapeChars(port_name, brkt_left, brkt_right,
'\0', escape);
port = cell->findLibertyPort(escaped_port_name); port = cell->findLibertyPort(escaped_port_name);
} }
return port; return port;

View File

@ -275,7 +275,7 @@ LibertyWriter::writeBusDcls()
sta::print(stream_, " type (\"{}\") {{\n", dcl->name()); sta::print(stream_, " type (\"{}\") {{\n", dcl->name());
sta::print(stream_, " base_type : array;\n"); sta::print(stream_, " base_type : array;\n");
sta::print(stream_, " data_type : bit;\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_from : {};\n", dcl->from());
sta::print(stream_, " bit_to : {};\n", dcl->to()); sta::print(stream_, " bit_to : {};\n", dcl->to());
sta::print(stream_, " }}\n"); sta::print(stream_, " }}\n");

View File

@ -175,6 +175,7 @@ TimingArc::intrinsicDelay() const
TimingArcAttrsPtr TimingArcSet::wire_timing_arc_attrs_ = nullptr; TimingArcAttrsPtr TimingArcSet::wire_timing_arc_attrs_ = nullptr;
TimingArcSet *TimingArcSet::wire_timing_arc_set_ = nullptr; TimingArcSet *TimingArcSet::wire_timing_arc_set_ = nullptr;
TimingArcSet *TimingArcSet::port_refpin_timing_arc_set_ = nullptr;
TimingArcSet::TimingArcSet(LibertyCell *, TimingArcSet::TimingArcSet(LibertyCell *,
LibertyPort *from, LibertyPort *from,
@ -524,6 +525,9 @@ TimingArcSet::init()
Transition::rise(), nullptr); Transition::rise(), nullptr);
new TimingArc(wire_timing_arc_set_, Transition::fall(), new TimingArc(wire_timing_arc_set_, Transition::fall(),
Transition::fall(), nullptr); Transition::fall(), nullptr);
port_refpin_timing_arc_set_ = new TimingArcSet(TimingRole::portDelayRefPin(),
wire_timing_arc_attrs_);
} }
void void
@ -532,6 +536,9 @@ TimingArcSet::destroy()
delete wire_timing_arc_set_; delete wire_timing_arc_set_;
wire_timing_arc_set_ = nullptr; wire_timing_arc_set_ = nullptr;
wire_timing_arc_attrs_ = nullptr; wire_timing_arc_attrs_ = nullptr;
delete port_refpin_timing_arc_set_;
port_refpin_timing_arc_set_ = nullptr;
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////

View File

@ -90,6 +90,8 @@ const TimingRole TimingRole::clock_tree_path_min_("min clock tree path", false,
false, MinMax::min(), nullptr, 27); false, MinMax::min(), nullptr, 27);
const TimingRole TimingRole::clock_tree_path_max_("max clock tree path", false, false, const TimingRole TimingRole::clock_tree_path_max_("max clock tree path", false, false,
false, MinMax::max(), nullptr, 28); false, MinMax::max(), nullptr, 28);
const TimingRole TimingRole::port_delay_ref_pin_("port delay ref pin", false, false,
false, nullptr, nullptr, 29);
TimingRole::TimingRole(const char *name, TimingRole::TimingRole(const char *name,
bool is_sdf_iopath, bool is_sdf_iopath,

View File

@ -3230,7 +3230,7 @@ TEST(TestCellTest, HasInferedRegTimingArcs) {
TEST(TestCellTest, HasSequentials) { TEST(TestCellTest, HasSequentials) {
LibertyLibrary lib("test_lib", "test.lib"); LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "CELL1", "test.lib"); TestCell cell(&lib, "CELL1", "test.lib");
EXPECT_FALSE(cell.hasSequentials()); EXPECT_FALSE(cell.isSequential());
} }
TEST(TestCellTest, SequentialsEmpty) { TEST(TestCellTest, SequentialsEmpty) {

View File

@ -810,7 +810,7 @@ TEST_F(StaLibertyTest, LibraryDelayModelType) {
TEST_F(StaLibertyTest, CellHasSequentials) { TEST_F(StaLibertyTest, CellHasSequentials) {
LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); LibertyCell *dff = lib_->findLibertyCell("DFF_X1");
if (dff) { if (dff) {
EXPECT_TRUE(dff->hasSequentials()); EXPECT_TRUE(dff->isSequential());
auto &seqs = dff->sequentials(); auto &seqs = dff->sequentials();
EXPECT_GT(seqs.size(), 0u); EXPECT_GT(seqs.size(), 0u);
} }
@ -3266,10 +3266,10 @@ TEST_F(StaLibertyTest, CellIsInverter2) {
TEST_F(StaLibertyTest, CellHasSequentials2) { TEST_F(StaLibertyTest, CellHasSequentials2) {
LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); LibertyCell *buf = lib_->findLibertyCell("BUF_X1");
ASSERT_NE(buf, nullptr); ASSERT_NE(buf, nullptr);
EXPECT_FALSE(buf->hasSequentials()); EXPECT_FALSE(buf->isSequential());
LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); LibertyCell *dff = lib_->findLibertyCell("DFF_X1");
if (dff) { if (dff) {
EXPECT_TRUE(dff->hasSequentials()); EXPECT_TRUE(dff->isSequential());
} }
} }

View File

@ -2166,14 +2166,14 @@ TEST_F(StaLibertyTest, CellSetClockGateType) {
TEST_F(StaLibertyTest, CellHasSequentialsBuf) { TEST_F(StaLibertyTest, CellHasSequentialsBuf) {
LibertyCell *buf = lib_->findLibertyCell("BUF_X1"); LibertyCell *buf = lib_->findLibertyCell("BUF_X1");
ASSERT_NE(buf, nullptr); ASSERT_NE(buf, nullptr);
EXPECT_FALSE(buf->hasSequentials()); EXPECT_FALSE(buf->isSequential());
} }
// LibertyCell::hasSequentials on DFF // LibertyCell::hasSequentials on DFF
TEST_F(StaLibertyTest, CellHasSequentialsDFF) { TEST_F(StaLibertyTest, CellHasSequentialsDFF) {
LibertyCell *dff = lib_->findLibertyCell("DFF_X1"); LibertyCell *dff = lib_->findLibertyCell("DFF_X1");
ASSERT_NE(dff, nullptr); ASSERT_NE(dff, nullptr);
EXPECT_TRUE(dff->hasSequentials()); EXPECT_TRUE(dff->isSequential());
} }
// LibertyCell::sequentials // LibertyCell::sequentials

View File

@ -3688,7 +3688,7 @@ library(test_r11_latch) {
LibertyCell *cell = lib->findLibertyCell("LATCH1"); LibertyCell *cell = lib->findLibertyCell("LATCH1");
EXPECT_NE(cell, nullptr); EXPECT_NE(cell, nullptr);
if (cell) { if (cell) {
EXPECT_TRUE(cell->hasSequentials()); EXPECT_TRUE(cell->isSequential());
} }
} }
remove(tmp_path.c_str()); remove(tmp_path.c_str());
@ -4338,7 +4338,7 @@ library(test_mbff_statetable) {
// But it has a statetable, so it IS sequential. // But it has a statetable, so it IS sequential.
EXPECT_NE(mbff->statetable(), nullptr); EXPECT_NE(mbff->statetable(), nullptr);
// hasSequentials() must return true for statetable-only cells. // hasSequentials() must return true for statetable-only cells.
EXPECT_TRUE(mbff->hasSequentials()) EXPECT_TRUE(mbff->isSequential())
<< "MBFF2 uses statetable (no ff/latch) but hasSequentials() " << "MBFF2 uses statetable (no ff/latch) but hasSequentials() "
"returned false — statetable cells are misclassified as " "returned false — statetable cells are misclassified as "
"combinational"; "combinational";

View File

@ -42,8 +42,8 @@ ConcreteLibrary::ConcreteLibrary(std::string_view name,
std::string_view filename, std::string_view filename,
bool is_liberty) : bool is_liberty) :
name_(name), name_(name),
id_(ConcreteNetwork::nextObjectId()),
filename_(filename), filename_(filename),
id_(ConcreteNetwork::nextObjectId()),
is_liberty_(is_liberty) is_liberty_(is_liberty)
{ {
} }

View File

@ -636,6 +636,16 @@ Network::isLatchData(const Pin *pin) const
return false; return false;
} }
bool
Network::isLatchOutput(const Pin *pin) const
{
LibertyPort *port = libertyPort(pin);
if (port)
return port->isLatchOutput();
else
return false;
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
std::string std::string

View File

@ -167,9 +167,10 @@ parseBusName(std::string_view name,
std::string std::string
escapeChars(std::string_view token, escapeChars(std::string_view token,
const char ch1, char ch1,
const char ch2, char ch2,
const char escape) char ch3,
char escape)
{ {
std::string escaped; std::string escaped;
escaped.reserve(token.size()); escaped.reserve(token.size());
@ -184,7 +185,7 @@ escapeChars(std::string_view token,
else else
escaped += ch; escaped += ch;
} }
else if (ch == ch1 || ch == ch2) { else if (ch == ch1 || ch == ch2 || ch == ch3) {
escaped += escape; escaped += escape;
escaped += ch; escaped += ch;
} }

View File

@ -330,6 +330,13 @@ NetworkNameAdapter::memberIterator(const Port *port) const
return network_->memberIterator(port); return network_->memberIterator(port);
} }
Port *
NetworkNameAdapter::makePort(Cell *cell,
std::string_view name)
{
return network_edit_->makePort(cell, name);
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
ObjectId ObjectId
@ -736,6 +743,14 @@ SdcNetwork::busName(const Port *port) const
return staToSdc(network_->busName(port)); return staToSdc(network_->busName(port));
} }
Port *
SdcNetwork::makePort(Cell *cell,
std::string_view name)
{
std::string escaped_name = escapeDividerBrackets(name, network_edit_);
return network_edit_->makePort(cell, escaped_name);
}
std::string std::string
SdcNetwork::name(const Instance *instance) const SdcNetwork::name(const Instance *instance) const
{ {
@ -1073,7 +1088,7 @@ SdcNetwork::makeInstance(LibertyCell *cell,
std::string_view name, std::string_view name,
Instance *parent) Instance *parent)
{ {
std::string escaped_name = escapeDividers(std::string(name), this); std::string escaped_name = escapeDividerBrackets(name, this);
return network_edit_->makeInstance(cell, escaped_name, parent); return network_edit_->makeInstance(cell, escaped_name, parent);
} }
@ -1081,7 +1096,7 @@ Net *
SdcNetwork::makeNet(std::string_view name, SdcNetwork::makeNet(std::string_view name,
Instance *parent) Instance *parent)
{ {
std::string escaped_name = escapeDividers(std::string(name), this); std::string escaped_name = escapeDividerBrackets(name, this);
return network_edit_->makeNet(escaped_name, parent); return network_edit_->makeNet(escaped_name, parent);
} }
@ -1254,11 +1269,19 @@ SdcNetwork::visitMatches(const Instance *parent,
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
std::string
escapeDividerBrackets(std::string_view name,
const Network *network)
{
return escapeChars(name, network->pathDivider(), '[', ']',
network->pathEscape());
}
std::string std::string
escapeDividers(std::string_view name, escapeDividers(std::string_view name,
const Network *network) const Network *network)
{ {
return escapeChars(name, network->pathDivider(), '\0', return escapeChars(name, network->pathDivider(), '\0', '\0',
network->pathEscape()); network->pathEscape());
} }
@ -1266,7 +1289,7 @@ std::string
escapeBrackets(std::string_view name, escapeBrackets(std::string_view name,
const Network *network) const Network *network)
{ {
return escapeChars(name, '[', ']', network->pathEscape()); return escapeChars(name, '[', ']', '\0', network->pathEscape());
} }
} // namespace sta } // namespace sta

View File

@ -443,7 +443,7 @@ Power::power(const Scene *scene,
pad.incr(inst_power); pad.incr(inst_power);
else if (inClockNetwork(inst, clk_network)) else if (inClockNetwork(inst, clk_network))
clock.incr(inst_power); clock.incr(inst_power);
else if (cell->hasSequentials()) else if (cell->isSequential())
sequential.incr(inst_power); sequential.incr(inst_power);
else else
combinational.incr(inst_power); combinational.incr(inst_power);
@ -554,9 +554,12 @@ ActivitySrchPred::searchThru(Edge *edge,
{ {
const Sdc *sdc = mode->sdc(); const Sdc *sdc = mode->sdc();
const TimingRole *role = edge->role(); const TimingRole *role = edge->role();
return !(edge->role()->isTimingCheck() || sdc->isDisabledConstraint(edge) return !(edge->role()->isTimingCheck()
|| sdc->isDisabledCondDefault(edge) || edge->isBidirectInstPath() || sdc->isDisabledConstraint(edge)
|| edge->isDisabledLoop() || role == TimingRole::regClkToQ() || sdc->isDisabledCondDefault(edge)
|| edge->isBidirectInstPath()
|| edge->isDisabledLoop()
|| role == TimingRole::regClkToQ()
|| role->isLatchDtoQ()); || role->isLatchDtoQ());
} }
@ -692,7 +695,8 @@ PropActivityVisitor::visit(Vertex *vertex)
if (cell) { if (cell) {
LibertyCell *test_cell = cell->libertyCell()->testCell(); LibertyCell *test_cell = cell->libertyCell()->testCell();
if (network_->isLoad(pin)) { 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 {}", debugPrint(debug_, "power_activity", 3, "pending seq {}",
network_->pathName(inst)); network_->pathName(inst));
visited_regs_.insert(inst); visited_regs_.insert(inst);
@ -701,10 +705,8 @@ PropActivityVisitor::visit(Vertex *vertex)
if (cell->isClockGate()) { if (cell->isClockGate()) {
const Pin *enable, *clk, *gclk; const Pin *enable, *clk, *gclk;
power_->clockGatePins(inst, enable, clk, gclk); power_->clockGatePins(inst, enable, clk, gclk);
if (gclk) { if (gclk)
Vertex *gclk_vertex = graph_->pinDrvrVertex(gclk); visited_regs_.insert(inst);
bfs_->enqueue(gclk_vertex);
}
} }
} }
bfs_->enqueueAdjacentVertices(vertex, mode_); bfs_->enqueueAdjacentVertices(vertex, mode_);
@ -746,9 +748,9 @@ PropActivityVisitor::setActivityCheck(const Pin *pin,
max_change_ = duty_delta; max_change_ = duty_delta;
max_change_pin_ = pin; max_change_pin_ = pin;
} }
bool changed = density_delta > change_tolerance_ || duty_delta > change_tolerance_ bool changed = density_delta > change_tolerance_
|| activity.origin() != prev_activity.origin(); || duty_delta > change_tolerance_
; || activity.origin() != prev_activity.origin();
power_->setActivity(pin, activity); power_->setActivity(pin, activity);
return changed; return changed;
} }
@ -905,8 +907,9 @@ Power::ensureActivities(const Scene *scene)
// unless it has been set by command. // unless it has been set by command.
if (input_activity_.origin() == PwrActivityOrigin::unknown) { if (input_activity_.origin() == PwrActivityOrigin::unknown) {
float min_period = clockMinPeriod(scene_->mode()->sdc()); float min_period = clockMinPeriod(scene_->mode()->sdc());
float density = if (min_period == 0.0)
0.1 / (min_period != 0.0 ? min_period : units_->timeUnit()->scale()); min_period = units_->timeUnit()->scale();
float density = 0.1 / min_period;
input_activity_.set(density, 0.5, PwrActivityOrigin::input); input_activity_.set(density, 0.5, PwrActivityOrigin::input);
} }
ActivitySrchPred activity_srch_pred(this); ActivitySrchPred activity_srch_pred(this);
@ -918,7 +921,7 @@ Power::ensureActivities(const Scene *scene)
// Propagate activiities through registers. // Propagate activiities through registers.
InstanceSet regs = std::move(visitor.visitedRegs()); InstanceSet regs = std::move(visitor.visitedRegs());
int pass = 1; int pass = 1;
while (!regs.empty() && pass < max_activity_passes_) { while (!regs.empty() && pass <= max_activity_passes_) {
visitor.init(); visitor.init();
for (const Instance *reg : regs) for (const Instance *reg : regs)
// Propagate activiities across register D->Q. // Propagate activiities across register D->Q.
@ -927,8 +930,10 @@ Power::ensureActivities(const Scene *scene)
// combinational logic. // combinational logic.
bfs.visit(levelize_->maxLevel(), &visitor); bfs.visit(levelize_->maxLevel(), &visitor);
regs = std::move(visitor.visitedRegs()); regs = std::move(visitor.visitedRegs());
debugPrint(debug_, "power_activity", 1, "Pass {} change {:.2f} {}", pass, debugPrint(debug_, "power_activity", 1, "Pass {} change {:.2f} {}",
visitor.maxChange(), network_->pathName(visitor.maxChangePin())); pass,
visitor.maxChange(),
network_->pathName(visitor.maxChangePin()));
pass++; pass++;
} }
} }
@ -972,6 +977,8 @@ Power::seedRegOutputActivities(const Instance *inst,
const SequentialSeq &seqs = test_cell->sequentials(); const SequentialSeq &seqs = test_cell->sequentials();
seedRegOutputActivities(inst, test_cell, seqs, bfs); 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 void
@ -1566,8 +1590,12 @@ Power::findActivity(const Pin *pin)
if (activity && activity->origin() != PwrActivityOrigin::unknown) if (activity && activity->origin() != PwrActivityOrigin::unknown)
return *activity; return *activity;
const Clock *clk = findClk(pin); const Clock *clk = findClk(pin);
float duty = clockDuty(clk); if (clk) {
return PwrActivity(2.0 / clk->period(), duty, PwrActivityOrigin::clock); float duty = clockDuty(clk);
return PwrActivity(2.0 / clk->period(), duty, PwrActivityOrigin::clock);
}
else
return PwrActivity();
} }
else if (global_activity_.isSet()) else if (global_activity_.isSet())
return global_activity_; return global_activity_;

View File

@ -224,6 +224,8 @@ protected:
const LibertyCell *test_cell, const LibertyCell *test_cell,
const SequentialSeq &seqs, const SequentialSeq &seqs,
BfsFwdIterator &bfs); BfsFwdIterator &bfs);
void seedClkGateOutputActivities(const Instance *inst,
BfsFwdIterator &bfs);
PwrActivity evalActivity(FuncExpr *expr, PwrActivity evalActivity(FuncExpr *expr,
const Instance *inst); const Instance *inst);
PwrActivity evalActivity(FuncExpr *expr, PwrActivity evalActivity(FuncExpr *expr,

View File

@ -1326,7 +1326,7 @@ TEST_F(PowerDesignTest, SequentialCellClassification) {
Instance *inst = child_iter->next(); Instance *inst = child_iter->next();
LibertyCell *cell = network->libertyCell(inst); LibertyCell *cell = network->libertyCell(inst);
ASSERT_NE(cell, nullptr); ASSERT_NE(cell, nullptr);
if (cell->hasSequentials()) { if (cell->isSequential()) {
seq_count++; seq_count++;
} else { } else {
comb_count++; comb_count++;

View File

@ -87,6 +87,14 @@ PortDelay::refTransition() const
return RiseFall::rise(); return RiseFall::rise();
} }
void
PortDelay::setRefPinEdgesExist(bool exists)
{
ref_pin_edges_exist_ = exists;
}
////////////////////////////////////////////////////////////////
InputDelay::InputDelay(const Pin *pin, InputDelay::InputDelay(const Pin *pin,
const ClockEdge *clk_edge, const ClockEdge *clk_edge,
int index, int index,
@ -97,6 +105,8 @@ InputDelay::InputDelay(const Pin *pin,
findLeafLoadPins(pin, network, &leaf_pins_); findLeafLoadPins(pin, network, &leaf_pins_);
} }
////////////////////////////////////////////////////////////////
OutputDelay::OutputDelay(const Pin *pin, OutputDelay::OutputDelay(const Pin *pin,
const ClockEdge *clk_edge, const ClockEdge *clk_edge,
const Network *network) : const Network *network) :

View File

@ -115,12 +115,10 @@ Sdc::Sdc(Mode *mode,
cycle_acctings_(this), cycle_acctings_(this),
input_delay_pin_map_(PinIdLess(network_)), input_delay_pin_map_(PinIdLess(network_)),
input_delay_ref_pin_map_(PinIdLess(network_)),
input_delay_leaf_pin_map_(PinIdLess(network_)), input_delay_leaf_pin_map_(PinIdLess(network_)),
input_delay_internal_pin_map_(PinIdLess(network_)), input_delay_internal_pin_map_(PinIdLess(network_)),
output_delay_pin_map_(PinIdLess(network_)), output_delay_pin_map_(PinIdLess(network_)),
output_delay_ref_pin_map_(PinIdLess(network_)),
output_delay_leaf_pin_map_(PinIdLess(network_)), output_delay_leaf_pin_map_(PinIdLess(network_)),
port_ext_cap_map_(network_), port_ext_cap_map_(network_),
@ -191,9 +189,9 @@ Sdc::clear()
input_delays_.clear(); input_delays_.clear();
input_delay_pin_map_.clear(); input_delay_pin_map_.clear();
input_delay_index_ = 0; input_delay_index_ = 0;
input_delay_ref_pin_map_.clear();
input_delay_leaf_pin_map_.clear(); input_delay_leaf_pin_map_.clear();
input_delay_internal_pin_map_.clear(); input_delay_internal_pin_map_.clear();
have_input_delay_ref_pins_ = false;
output_delays_.clear(); output_delays_.clear();
output_delay_pin_map_.clear(); output_delay_pin_map_.clear();
@ -287,12 +285,10 @@ Sdc::deleteConstraints()
deleteContents(input_delays_); deleteContents(input_delays_);
deleteContents(input_delay_pin_map_); deleteContents(input_delay_pin_map_);
deleteContents(input_delay_leaf_pin_map_); deleteContents(input_delay_leaf_pin_map_);
deleteContents(input_delay_ref_pin_map_);
deleteContents(input_delay_internal_pin_map_); deleteContents(input_delay_internal_pin_map_);
deleteContents(output_delays_); deleteContents(output_delays_);
deleteContents(output_delay_pin_map_); deleteContents(output_delay_pin_map_);
deleteContents(output_delay_ref_pin_map_);
deleteContents(output_delay_leaf_pin_map_); deleteContents(output_delay_leaf_pin_map_);
deleteContents(clk_hpin_disables_); deleteContents(clk_hpin_disables_);
@ -2671,16 +2667,9 @@ Sdc::setInputDelay(const Pin *pin,
delays->setValue(rf, min_max, delay); delays->setValue(rf, min_max, delay);
} }
if (ref_pin) {
InputDelaySet *ref_inputs = findKey(input_delay_ref_pin_map_, ref_pin);
if (ref_inputs == nullptr) {
ref_inputs = new InputDelaySet;
input_delay_ref_pin_map_[ref_pin] = ref_inputs;
}
ref_inputs->insert(input_delay);
}
input_delay->setRefPin(ref_pin); input_delay->setRefPin(ref_pin);
if (ref_pin)
have_input_delay_ref_pins_ = true;
input_delay->setSourceLatencyIncluded(source_latency_included); input_delay->setSourceLatencyIncluded(source_latency_included);
input_delay->setNetworkLatencyIncluded(network_latency_included); input_delay->setNetworkLatencyIncluded(network_latency_included);
} }
@ -2766,12 +2755,6 @@ Sdc::deleteInputDelays(const Pin *pin,
} }
} }
InputDelaySet *
Sdc::refPinInputDelays(const Pin *ref_pin) const
{
return findKey(input_delay_ref_pin_map_, ref_pin);
}
InputDelaySet * InputDelaySet *
Sdc::inputDelaysLeafPin(const Pin *leaf_pin) const Sdc::inputDelaysLeafPin(const Pin *leaf_pin) const
{ {
@ -2828,17 +2811,47 @@ Sdc::swapPortDelays(Sdc *sdc1,
{ {
std::swap(sdc1->input_delays_, sdc2->input_delays_); std::swap(sdc1->input_delays_, sdc2->input_delays_);
std::swap(sdc1->input_delay_pin_map_, sdc2->input_delay_pin_map_); std::swap(sdc1->input_delay_pin_map_, sdc2->input_delay_pin_map_);
std::swap(sdc1->input_delay_ref_pin_map_, sdc2->input_delay_ref_pin_map_);
std::swap(sdc1->input_delay_leaf_pin_map_, sdc2->input_delay_leaf_pin_map_); std::swap(sdc1->input_delay_leaf_pin_map_, sdc2->input_delay_leaf_pin_map_);
std::swap(sdc1->input_delay_internal_pin_map_, sdc2->input_delay_internal_pin_map_); std::swap(sdc1->input_delay_internal_pin_map_, sdc2->input_delay_internal_pin_map_);
std::swap(sdc1->input_delay_index_, sdc2->input_delay_index_); std::swap(sdc1->input_delay_index_, sdc2->input_delay_index_);
std::swap(sdc1->output_delays_, sdc2->output_delays_); std::swap(sdc1->output_delays_, sdc2->output_delays_);
std::swap(sdc1->output_delay_pin_map_, sdc2->output_delay_pin_map_); std::swap(sdc1->output_delay_pin_map_, sdc2->output_delay_pin_map_);
std::swap(sdc1->output_delay_ref_pin_map_, sdc2->output_delay_ref_pin_map_);
std::swap(sdc1->output_delay_leaf_pin_map_, sdc2->output_delay_leaf_pin_map_); std::swap(sdc1->output_delay_leaf_pin_map_, sdc2->output_delay_leaf_pin_map_);
} }
void
Sdc::ensureInputDelayRefPinEdges()
{
if (have_input_delay_ref_pins_) {
for (InputDelay *input_delay : input_delays_) {
const Pin *ref_pin = input_delay->refPin();
if (ref_pin
&& !input_delay->refPinEdgesExist()) {
Vertex *ref_pin_vertex = graph_->pinLoadVertex(ref_pin);
for (const Pin *pin : input_delay->leafPins()) {
Vertex *input_vertex = graph_->pinDrvrVertex(pin);
graph_->makeEdge(ref_pin_vertex, input_vertex,
TimingArcSet::portRefPinTimingArcSet());
}
input_delay->setRefPinEdgesExist(true);
}
}
}
}
// 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 void
@ -2867,16 +2880,7 @@ Sdc::setOutputDelay(const Pin *pin,
delays->setValue(rf, min_max, delay); delays->setValue(rf, min_max, delay);
} }
if (ref_pin) {
OutputDelaySet *ref_outputs = findKey(output_delay_ref_pin_map_, ref_pin);
if (ref_outputs == nullptr) {
ref_outputs = new OutputDelaySet;
output_delay_ref_pin_map_[ref_pin] = ref_outputs;
}
ref_outputs->insert(output_delay);
}
output_delay->setRefPin(ref_pin); output_delay->setRefPin(ref_pin);
output_delay->setSourceLatencyIncluded(source_latency_included); output_delay->setSourceLatencyIncluded(source_latency_included);
output_delay->setNetworkLatencyIncluded(network_latency_included); output_delay->setNetworkLatencyIncluded(network_latency_included);
} }

View File

@ -45,7 +45,7 @@ proc_redirect read_sdc {
if { [info exists keys(-mode)] } { if { [info exists keys(-mode)] } {
set mode_name $keys(-mode) set mode_name $keys(-mode)
set prev_mode [cmd_mode] set prev_mode [cmd_mode_name]
try { try {
set_cmd_mode $mode_name set_cmd_mode $mode_name
include_file $filename $echo 0 include_file $filename $echo 0
@ -69,7 +69,7 @@ proc write_sdc { args } {
flags {-map_hpins -compatible -gzip -no_timestamp} flags {-map_hpins -compatible -gzip -no_timestamp}
check_argc_eq1 "write_sdc" $args check_argc_eq1 "write_sdc" $args
set mode [cmd_mode] set mode [cmd_mode_name]
if { [info exists keys(-mode)] } { if { [info exists keys(-mode)] } {
set mode $keys(-mode) set mode $keys(-mode)
} }

View File

@ -344,11 +344,7 @@ BfsIterator::remove(Vertex *vertex)
BfsFwdIterator::BfsFwdIterator(BfsIndex bfs_index, BfsFwdIterator::BfsFwdIterator(BfsIndex bfs_index,
SearchPred *search_pred, SearchPred *search_pred,
StaState *sta) : StaState *sta) :
BfsIterator(bfs_index, BfsIterator(bfs_index, 0, Graph::vertex_level_max, search_pred, sta)
0,
level_max,
search_pred,
sta)
{ {
} }
@ -416,11 +412,7 @@ BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex,
BfsBkwdIterator::BfsBkwdIterator(BfsIndex bfs_index, BfsBkwdIterator::BfsBkwdIterator(BfsIndex bfs_index,
SearchPred *search_pred, SearchPred *search_pred,
StaState *sta) : StaState *sta) :
BfsIterator(bfs_index, BfsIterator(bfs_index, Graph::vertex_level_max, 0, search_pred, sta)
level_max,
0,
search_pred,
sta)
{ {
} }

View File

@ -26,7 +26,6 @@
#include <cmath> #include <cmath>
#include "Bfs.hh"
#include "Clock.hh" #include "Clock.hh"
#include "ContainerHelpers.hh" #include "ContainerHelpers.hh"
#include "Debug.hh" #include "Debug.hh"
@ -270,30 +269,38 @@ Genclks::ensureMaster(Clock *gclk,
} }
if (!found_master) { if (!found_master) {
// Search backward from generated clock source pin to a clock pin. // Search backward from generated clock source pin to a clock pin.
GenClkMasterSearchPred pred(this); GenClkMasterSearchPred srch_pred(this);
BfsBkwdIterator iter(BfsIndex::other, &pred, this); VertexQueue master_queue;
seedSrcPins(gclk, iter); VertexSet visited = makeVertexSet(this);
while (iter.hasNext()) { VertexSet src_vertices = makeVertexSet(this);
Vertex *vertex = iter.next(); gclk->srcPinVertices(src_vertices, network_, graph_);
Pin *pin = vertex->pin(); for (Vertex *vertex : src_vertices)
if (sdc->isLeafPinClock(pin)) { master_queue.push(vertex);
ClockSet *master_clks = sdc->findLeafPinClocks(pin); while (!master_queue.empty()) {
if (master_clks) { Vertex *vertex = master_queue.front();
ClockSet::iterator master_iter = master_clks->begin(); master_queue.pop();
if (master_iter != master_clks->end()) { if (!visited.contains(vertex)) {
master_clk = *master_iter++; visited.insert(vertex);
// Master source pin can actually be a clock source pin. Pin *pin = vertex->pin();
if (master_clk != gclk) { if (sdc->isLeafPinClock(pin)) {
gclk->setInferedMasterClk(master_clk); ClockSet *master_clks = sdc->findLeafPinClocks(pin);
debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(), if (master_clks) {
master_clk->name()); ClockSet::iterator master_iter = master_clks->begin();
master_clk_count++; if (master_iter != master_clks->end()) {
break; 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) 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 // Similar to ClkTreeSearchPred but
@ -349,32 +346,34 @@ Genclks::findFanin(Clock *gclk,
{ {
// Search backward from generated clock source pin to a clock pin. // Search backward from generated clock source pin to a clock pin.
GenClkFaninSrchPred srch_pred(gclk, this); GenClkFaninSrchPred srch_pred(gclk, this);
BfsBkwdIterator iter(BfsIndex::other, &srch_pred, this); VertexQueue fanin_queue;
seedClkVertices(gclk, iter, fanins); seedClkVertices(gclk, fanin_queue, fanins, srch_pred);
while (iter.hasNext()) { while (!fanin_queue.empty()) {
Vertex *vertex = iter.next(); Vertex *vertex = fanin_queue.front();
fanin_queue.pop();
if (!fanins.contains(vertex)) { if (!fanins.contains(vertex)) {
fanins.insert(vertex); fanins.insert(vertex);
debugPrint(debug_, "genclk", 2, "gen clk {} fanin {}", gclk->name(), debugPrint(debug_, "genclk", 2, "gen clk {} fanin {}", gclk->name(),
vertex->to_string(this)); vertex->to_string(this));
iter.enqueueAdjacentVertices(vertex, mode_); enqueueFanin(vertex, fanin_queue, srch_pred);
} }
} }
} }
void void
Genclks::seedClkVertices(Clock *clk, Genclks::seedClkVertices(Clock *clk,
BfsBkwdIterator &iter, VertexQueue &fanin_queue,
VertexSet &fanins) VertexSet &fanins,
SearchPred &srch_pred)
{ {
for (const Pin *pin : clk->leafPins()) { for (const Pin *pin : clk->leafPins()) {
Vertex *vertex, *bidirect_drvr_vertex; Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
fanins.insert(vertex); fanins.insert(vertex);
iter.enqueueAdjacentVertices(vertex, mode_); enqueueFanin(vertex, fanin_queue, srch_pred);
if (bidirect_drvr_vertex) { if (bidirect_drvr_vertex) {
fanins.insert(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()); debugPrint(debug_, "genclk", 2, "find gen clk {} insertion", gclk->name());
GenclkInfo *genclk_info = makeGenclkInfo(gclk); GenclkInfo *genclk_info = makeGenclkInfo(gclk);
FilterPath *src_filter = genclk_info->srcFilter(); FilterPath *src_filter = genclk_info->srcFilter();
VertexQueue insert_queue;
GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this); GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this);
BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this); seedSrcPins(gclk, src_filter, insert_queue, srch_pred);
seedSrcPins(gclk, src_filter, insert_iter);
// Propagate arrivals to generated clk root pin level. // 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. // Unregister the filter so that it is not triggered by other searches.
// The exception itself has to stick around because the source path // The exception itself has to stick around because the source path
// tags reference it. // tags reference it.
@ -591,7 +590,8 @@ Genclks::makeSrcFilter(Clock *gclk,
void void
Genclks::seedSrcPins(Clock *gclk, Genclks::seedSrcPins(Clock *gclk,
FilterPath *src_filter, FilterPath *src_filter,
BfsFwdIterator &insert_iter) VertexQueue &insert_queue,
SearchPred &srch_pred)
{ {
Clock *master_clk = gclk->masterClk(); Clock *master_clk = gclk->masterClk();
for (const Pin *master_pin : master_clk->leafPins()) { for (const Pin *master_pin : master_clk->leafPins()) {
@ -613,13 +613,35 @@ Genclks::seedSrcPins(Clock *gclk,
tag_bldr.setArrival(tag, insert); 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 * Tag *
Genclks::makeTag(const Clock *gclk, Genclks::makeTag(const Clock *gclk,
const Clock *master_clk, const Clock *master_clk,
@ -637,11 +659,12 @@ Genclks::makeTag(const Clock *gclk,
state = state->nextState(); state = state->nextState();
ExceptionStateSet *states = new ExceptionStateSet(); ExceptionStateSet *states = new ExceptionStateSet();
states->insert(state); states->insert(state);
const ClkInfo *clk_info = search_->findClkInfo( const ClkInfo *clk_info = search_->findClkInfo(scene, master_clk->edge(master_rf),
scene, master_clk->edge(master_rf), master_pin, true, nullptr, true, nullptr, master_pin, true, nullptr, true,
insert, 0.0, nullptr, min_max, nullptr); nullptr, insert, 0.0, nullptr,
return search_->findTag(scene, master_rf, min_max, clk_info, false, nullptr, false, min_max, nullptr);
states, true, nullptr); return search_->findTag(scene, master_rf, min_max, clk_info, false, nullptr,
false, states, true, nullptr);
} }
class GenClkArrivalSearchPred : public EvalPred class GenClkArrivalSearchPred : public EvalPred
@ -690,8 +713,8 @@ class GenclkSrcArrivalVisitor : public ArrivalVisitor
{ {
public: public:
GenclkSrcArrivalVisitor(Clock *gclk, GenclkSrcArrivalVisitor(Clock *gclk,
BfsFwdIterator *insert_iter,
GenclkInfo *genclk_info, GenclkInfo *genclk_info,
VertexQueue &insert_queue,
const Mode *mode); const Mode *mode);
GenclkSrcArrivalVisitor(const GenclkSrcArrivalVisitor &visitor); GenclkSrcArrivalVisitor(const GenclkSrcArrivalVisitor &visitor);
VertexVisitor *copy() const override; VertexVisitor *copy() const override;
@ -699,7 +722,7 @@ public:
protected: protected:
Clock *gclk_; Clock *gclk_;
BfsFwdIterator *insert_iter_; VertexQueue &insert_queue_;
GenclkInfo *genclk_info_; GenclkInfo *genclk_info_;
GenClkInsertionSearchPred srch_pred_; GenClkInsertionSearchPred srch_pred_;
const Mode *mode_; const Mode *mode_;
@ -708,12 +731,12 @@ protected:
}; };
GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk, GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk,
BfsFwdIterator *insert_iter,
GenclkInfo *genclk_info, GenclkInfo *genclk_info,
VertexQueue &insert_queue,
const Mode *mode) : const Mode *mode) :
ArrivalVisitor(mode->sta()), ArrivalVisitor(mode->sta()),
gclk_(gclk), gclk_(gclk),
insert_iter_(insert_iter), insert_queue_(insert_queue),
genclk_info_(genclk_info), genclk_info_(genclk_info),
srch_pred_(gclk_, genclk_info, mode->sta()), srch_pred_(gclk_, genclk_info, mode->sta()),
mode_(mode), mode_(mode),
@ -725,7 +748,7 @@ GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk,
GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(const GenclkSrcArrivalVisitor &visitor) : GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(const GenclkSrcArrivalVisitor &visitor) :
ArrivalVisitor(visitor), ArrivalVisitor(visitor),
gclk_(visitor.gclk_), gclk_(visitor.gclk_),
insert_iter_(visitor.insert_iter_), insert_queue_(visitor.insert_queue_),
genclk_info_(visitor.genclk_info_), genclk_info_(visitor.genclk_info_),
srch_pred_(visitor.gclk_, visitor.genclk_info_, this), srch_pred_(visitor.gclk_, visitor.genclk_info_, this),
mode_(visitor.mode_), mode_(visitor.mode_),
@ -748,24 +771,28 @@ GenclkSrcArrivalVisitor::visit(Vertex *vertex)
tag_bldr_->init(vertex); tag_bldr_->init(vertex);
has_fanin_one_ = graph_->hasFaninOne(vertex); has_fanin_one_ = graph_->hasFaninOne(vertex);
genclks_->copyGenClkSrcPaths(vertex, tag_bldr_); genclks_->copyGenClkSrcPaths(vertex, tag_bldr_);
visitFaninPaths(vertex); visitFaninPaths(vertex, true);
// Propagate beyond the clock tree to reach generated clk roots.
insert_iter_->enqueueAdjacentVertices(vertex, &srch_pred_, mode_);
search_->setVertexArrivals(vertex, tag_bldr_); search_->setVertexArrivals(vertex, tag_bldr_);
// Propagate beyond the clock tree to reach generated clk roots.
genclks_->enqueueFanout(vertex, insert_queue_, srch_pred_);
} }
void void
Genclks::findSrcArrivals(Clock *gclk, Genclks::findSrcArrivals(Clock *gclk,
BfsFwdIterator &insert_iter, GenclkInfo *genclk_info,
GenclkInfo *genclk_info) VertexQueue &insert_queue)
{ {
GenClkArrivalSearchPred eval_pred(gclk, this); 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); arrival_visitor.init(true, false, &eval_pred);
// This cannot restrict the search level because loops in the clock tree // This cannot restrict the search level because loops in the clock tree
// can circle back to the generated clock src pin. // can circle back to the generated clock src pin.
// Parallel visit is slightly slower (at last check). // 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. // Copy generated clock source paths to tag_bldr.
@ -886,15 +913,15 @@ void
Genclks::deleteGenclkSrcPaths(Clock *gclk) Genclks::deleteGenclkSrcPaths(Clock *gclk)
{ {
GenclkInfo *genclk_info = genclkInfo(gclk); GenclkInfo *genclk_info = genclkInfo(gclk);
GenClkInsertionSearchPred srch_pred(gclk, genclk_info, mode_->sta()); VertexQueue insert_queue;
BfsFwdIterator insert_iter(BfsIndex::other, &srch_pred, this); GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this);
FilterPath *src_filter = genclk_info->srcFilter(); FilterPath *src_filter = genclk_info->srcFilter();
seedSrcPins(gclk, src_filter, insert_iter); seedSrcPins(gclk, src_filter, insert_queue, srch_pred);
GenClkArrivalSearchPred eval_pred(gclk, this); while (!insert_queue.empty()) {
while (insert_iter.hasNext()) { Vertex *vertex = insert_queue.front();
Vertex *vertex = insert_iter.next(); insert_queue.pop();
search_->deletePaths(vertex); search_->deletePaths(vertex);
insert_iter.enqueueAdjacentVertices(vertex, &srch_pred, mode_); enqueueFanout(vertex, insert_queue, srch_pred);
} }
} }

View File

@ -26,6 +26,7 @@
#include <cstddef> #include <cstddef>
#include <map> #include <map>
#include <queue>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -42,8 +43,6 @@
namespace sta { namespace sta {
class GenclkInfo; class GenclkInfo;
class BfsFwdIterator;
class BfsBkwdIterator;
class SearchPred; class SearchPred;
class TagGroupBldr; class TagGroupBldr;
@ -59,6 +58,7 @@ public:
using GenclkInfoMap = std::map<Clock*, GenclkInfo*>; using GenclkInfoMap = std::map<Clock*, GenclkInfo*>;
using GenclkSrcPathMap = std::map<ClockPinPair, std::vector<Path>, ClockPinPairLess>; using GenclkSrcPathMap = std::map<ClockPinPair, std::vector<Path>, ClockPinPairLess>;
using VertexGenclkSrcPathsMap = std::map<Vertex*, std::vector<const Path*>, VertexIdLess>; using VertexGenclkSrcPathsMap = std::map<Vertex*, std::vector<const Path*>, VertexIdLess>;
using VertexQueue = std::queue<Vertex*>;
class Genclks : public StaState class Genclks : public StaState
{ {
@ -102,18 +102,20 @@ private:
void recordSrcPaths(Clock *gclk); void recordSrcPaths(Clock *gclk);
void findInsertionDelays(Clock *gclk); void findInsertionDelays(Clock *gclk);
void seedClkVertices(Clock *clk, void seedClkVertices(Clock *clk,
BfsBkwdIterator &iter, VertexQueue &fanin_queue,
VertexSet &dfanins); VertexSet &fanins,
SearchPred &srch_pred);
size_t srcPathIndex(const RiseFall *clk_rf, size_t srcPathIndex(const RiseFall *clk_rf,
const MinMax *min_max) const; const MinMax *min_max) const;
bool matchesSrcFilter(Path *path, bool matchesSrcFilter(Path *path,
const Clock *gclk) const; const Clock *gclk) const;
void seedSrcPins(Clock *gclk, void seedSrcPins(Clock *gclk,
FilterPath *src_filter, FilterPath *src_filter,
BfsFwdIterator &insert_iter); VertexQueue &insert_queue,
SearchPred &srch_pred);
void findSrcArrivals(Clock *gclk, void findSrcArrivals(Clock *gclk,
BfsFwdIterator &insert_iter, GenclkInfo *genclk_info,
GenclkInfo *genclk_info); VertexQueue &insert_queue);
FilterPath *makeSrcFilter(Clock *gclk, FilterPath *makeSrcFilter(Clock *gclk,
Sdc *sdc); Sdc *sdc);
void deleteGenClkInfo(); void deleteGenClkInfo();
@ -125,9 +127,13 @@ private:
Arrival insert, Arrival insert,
Scene *scene, Scene *scene,
const MinMax *min_max); const MinMax *min_max);
void seedSrcPins(Clock *clk,
BfsBkwdIterator &iter);
void findInsertionDelay(Clock *gclk); 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); GenclkInfo *makeGenclkInfo(Clock *gclk);
FilterPath *srcFilter(Clock *gclk); FilterPath *srcFilter(Clock *gclk);
void findFanin(Clock *gclk, void findFanin(Clock *gclk,
@ -147,6 +153,8 @@ private:
GenclkSrcPathMap genclk_src_paths_; GenclkSrcPathMap genclk_src_paths_;
GenclkInfoMap genclk_info_map_; GenclkInfoMap genclk_info_map_;
VertexGenclkSrcPathsMap vertex_src_paths_map_; VertexGenclkSrcPathsMap vertex_src_paths_map_;
friend class GenclkSrcArrivalVisitor;
}; };
} // namespace sta } // namespace sta

View File

@ -359,16 +359,7 @@ Latches::latchOutArrival(const Path *data_path,
arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge, arc_delay = search_->deratedDelay(data_vertex, d_q_arc, d_q_edge,
false, min_max, dcalc_ap, sdc); false, min_max, dcalc_ap, sdc);
q_arrival = delaySum(data_path->arrival(), arc_delay, this); q_arrival = delaySum(data_path->arrival(), arc_delay, this);
// Copy the data tag but remove the drprClkPath. // Copy the data tag but remove the crprClkPath.
// 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.
// Kill the crprClklPath to be safe. // Kill the crprClklPath to be safe.
const ClkInfo *data_clk_info = data_path->clkInfo(this); const ClkInfo *data_clk_info = data_path->clkInfo(this);
const ClkInfo *q_clk_info = const ClkInfo *q_clk_info =

View File

@ -118,6 +118,9 @@ Levelize::findLevels()
if (observer_) if (observer_)
observer_->levelsChangedBefore(); observer_->levelsChangedBefore();
for (const Mode *mode : modes_)
mode->sdc()->ensureInputDelayRefPinEdges();
VertexIterator vertex_iter(graph_); VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) { while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next(); Vertex *vertex = vertex_iter.next();
@ -132,7 +135,6 @@ Levelize::findLevels()
findBackEdges(); findBackEdges();
VertexSeq topo_sorted = findTopologicalOrder(); VertexSeq topo_sorted = findTopologicalOrder();
assignLevels(topo_sorted); assignLevels(topo_sorted);
ensureLatchLevels();
// Set level of stranded vertices (constants) to zero. // Set level of stranded vertices (constants) to zero.
VertexIterator vertex_iter2(graph_); VertexIterator vertex_iter2(graph_);
@ -185,20 +187,20 @@ Levelize::isRoot(Vertex *vertex)
if (searchThru(edge)) if (searchThru(edge))
return false; return false;
} }
// Levelize bidirect driver as if it was a fanout of the bidirect load. return true;
return !(graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin())
&& vertex->isBidirectDriver());
} }
bool bool
Levelize::searchThru(Edge *edge) Levelize::searchThru(Edge *edge)
{ {
const TimingRole *role = edge->role(); const TimingRole *role = edge->role();
return !role->isTimingCheck() && role != TimingRole::latchDtoQ() return !(role->isTimingCheck()
&& !edge->isDisabledLoop() || role == TimingRole::latchDtoQ()
// Register/latch preset/clr edges are disabled by default. || edge->isDisabledLoop()
&& !(role == TimingRole::regSetClr() && !variables_->presetClrArcsEnabled()) // Register/latch preset/clr edges are disabled by default.
&& !(edge->isBidirectInstPath() && !variables_->bidirectInstPathsEnabled()); || (role == TimingRole::regSetClr()
&& !variables_->presetClrArcsEnabled())
|| isDisabledBidirectInstPath(edge));
} }
bool bool
@ -352,8 +354,6 @@ Levelize::findTopologicalOrder()
Vertex *to_vertex = edge->to(graph_); Vertex *to_vertex = edge->to(graph_);
if (searchThru(edge)) if (searchThru(edge))
in_degree[to_vertex] += 1; 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. // Levelize bidirect driver as if it was a fanout of the bidirect load.
const Pin *pin = vertex->pin(); const Pin *pin = vertex->pin();
@ -505,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 void
Levelize::setLevel(Vertex *vertex, Levelize::setLevel(Vertex *vertex,
Level level) Level level)
@ -602,7 +581,6 @@ Levelize::relevelize()
EdgeSeq path; EdgeSeq path;
visit(vertex, nullptr, vertex->level(), 1, path_vertices, path); visit(vertex, nullptr, vertex->level(), 1, path_vertices, path);
} }
ensureLatchLevels();
levels_valid_ = true; levels_valid_ = true;
relevelize_from_.clear(); relevelize_from_.clear();
} }
@ -633,18 +611,6 @@ Levelize::visit(Vertex *vertex,
visit(to_vertex, edge, level + level_space, level_space, path_vertices, visit(to_vertex, edge, level + level_space, level_space, path_vertices,
path); 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. // Levelize bidirect driver as if it was a fanout of the bidirect load.

View File

@ -83,7 +83,6 @@ protected:
EdgeSeq &path); EdgeSeq &path);
EdgeSeq *loopEdges(EdgeSeq &path, EdgeSeq *loopEdges(EdgeSeq &path,
Edge *closing_edge); Edge *closing_edge);
void ensureLatchLevels();
void findBackEdges(); void findBackEdges();
EdgeSet findBackEdges(EdgeSeq &path, EdgeSet findBackEdges(EdgeSeq &path,
FindBackEdgesStack &stack); FindBackEdgesStack &stack);
@ -113,7 +112,6 @@ protected:
GraphLoopSeq loops_; GraphLoopSeq loops_;
EdgeSet loop_edges_; EdgeSet loop_edges_;
EdgeSet disabled_loop_edges_; EdgeSet disabled_loop_edges_;
EdgeSet latch_d_to_q_edges_;
LevelizeObserver *observer_{nullptr}; LevelizeObserver *observer_{nullptr};
}; };

View File

@ -238,6 +238,28 @@ PathEnum::reportDiversionPath(Diversion *div)
using VisitedFanins = std::set<std::pair<const Vertex *, const TimingArc *>>; using VisitedFanins = std::set<std::pair<const Vertex *, const TimingArc *>>;
using VertexEdge = std::pair<const Vertex *, const RiseFall *>; 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 class PathEnumFaninVisitor : public PathVisitor
{ {
public: public:
@ -247,6 +269,7 @@ public:
bool unique_edges, bool unique_edges,
PathEnum *path_enum); PathEnum *path_enum);
PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor); PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor);
virtual ~PathEnumFaninVisitor();
VertexVisitor *copy() const override; VertexVisitor *copy() const override;
void visitFaninPathsThru(Path *before_div, void visitFaninPathsThru(Path *before_div,
Vertex *prev_vertex, Vertex *prev_vertex,
@ -311,7 +334,7 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end,
bool unique_pins, bool unique_pins,
bool unique_edges, bool unique_edges,
PathEnum *path_enum) : PathEnum *path_enum) :
PathVisitor(path_enum), PathVisitor(new EnumPred(path_enum), false, path_enum),
path_end_(path_end), path_end_(path_end),
before_div_(before_div), before_div_(before_div),
unique_pins_(unique_pins), unique_pins_(unique_pins),
@ -334,6 +357,11 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor)
{ {
} }
PathEnumFaninVisitor::~PathEnumFaninVisitor()
{
delete pred_;
}
VertexVisitor * VertexVisitor *
PathEnumFaninVisitor::copy() const PathEnumFaninVisitor::copy() const
{ {
@ -356,7 +384,7 @@ PathEnumFaninVisitor::visitFaninPathsThru(Path *before_div,
prev_vertex_ = prev_vertex; prev_vertex_ = prev_vertex;
visited_fanins_.clear(); visited_fanins_.clear();
unique_edge_divs_.clear(); unique_edge_divs_.clear();
visitFaninPaths(before_div_->vertex(this)); visitFaninPaths(before_div_->vertex(this), true);
if (unique_edges_) { if (unique_edges_) {
for (auto [vertex_edge, div] : unique_edge_divs_) for (auto [vertex_edge, div] : unique_edge_divs_)
@ -381,7 +409,8 @@ PathEnumFaninVisitor::visitEdge(const Pin *from_pin,
Path *from_path = from_iter.next(); Path *from_path = from_iter.next();
const Mode *mode = from_path->mode(this); const Mode *mode = from_path->mode(this);
const Sdc *sdc = mode->sdc(); 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) && pred_->searchTo(to_vertex, mode)
// Fanin paths are broken by path delay internal pin startpoints. // Fanin paths are broken by path delay internal pin startpoints.
&& !sdc->isPathDelayInternalFromBreak(to_pin)) { && !sdc->isPathDelayInternalFromBreak(to_pin)) {
@ -421,6 +450,14 @@ PathEnumFaninVisitor::visitFromToPath(const Pin *,
Arrival & /* to_arrival */, Arrival & /* to_arrival */,
const MinMax *) 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. // These paths fanin to before_div_ so we know to_vertex matches.
if ((!unique_pins_ || from_vertex != prev_vertex_) if ((!unique_pins_ || from_vertex != prev_vertex_)
&& (!unique_edges_ || from_vertex != prev_vertex_ && (!unique_edges_ || from_vertex != prev_vertex_
@ -646,10 +683,10 @@ PathEnum::makeDivertedPath(Path *path,
Path *prev_copy = nullptr; Path *prev_copy = nullptr;
while (p) { while (p) {
// prev_path made in next pass. // prev_path made in next pass.
Path *copy = Path *copy = new Path(p->vertex(this), p->tag(this), p->arrival(),
new Path(p->vertex(this), p->tag(this), p->arrival(), // Replaced on next pass.
// Replaced on next pass. p->prevPath(), p->prevEdge(this), p->prevArc(this),
p->prevPath(), p->prevEdge(this), p->prevArc(this), true, this); true, this);
search_->saveEnumPath(copy); search_->saveEnumPath(copy);
if (prev_copy) if (prev_copy)
prev_copy->setPrevPath(copy); prev_copy->setPrevPath(copy);

View File

@ -474,23 +474,23 @@ PathGroups::pathGroups(const PathEnd *path_end) const
} }
// Mirrors PathGroups::pathGroup. // Mirrors PathGroups::pathGroup.
StringSeq bool
PathGroups::pathGroupNames(const PathEnd *path_end, PathGroups::inPathGroupNamed(const PathEnd *path_end,
const StaState *sta) std::string_view path_group_name,
const StaState *sta)
{ {
StringSeq group_names;
std::string group_name;
const Search *search = sta->search(); const Search *search = sta->search();
ExceptionPathSeq group_paths = search->groupPathsTo(path_end); ExceptionPathSeq group_paths = search->groupPathsTo(path_end);
if (path_end->isUnconstrained()) if (path_end->isUnconstrained())
group_name = unconstrained_group_name_; return path_group_name == unconstrained_group_name_;
else if (!group_paths.empty()) { else if (!group_paths.empty()) {
// GroupPaths have precedence. // GroupPaths have precedence.
for (ExceptionPath *group_path : group_paths) { for (ExceptionPath *group_path : group_paths) {
if (group_path->isDefault()) std::string_view group_name = group_path->isDefault()
group_names.emplace_back(path_delay_group_name_); ? path_delay_group_name_
else : group_path->name();
group_names.emplace_back(group_path->name()); if (path_group_name == group_name)
return true;;
} }
} }
else if (path_end->isCheck() || path_end->isLatchCheck()) { else if (path_end->isCheck() || path_end->isLatchCheck()) {
@ -498,18 +498,18 @@ PathGroups::pathGroupNames(const PathEnd *path_end,
const Clock *tgt_clk = path_end->targetClk(sta); const Clock *tgt_clk = path_end->targetClk(sta);
if (check_role == TimingRole::removal() if (check_role == TimingRole::removal()
|| check_role == TimingRole::recovery()) || check_role == TimingRole::recovery())
group_name = async_group_name_; return path_group_name == async_group_name_;
else else
group_name = tgt_clk->name(); return path_group_name == tgt_clk->name();
} }
else if (path_end->isOutputDelay() else if (path_end->isOutputDelay()
|| path_end->isDataCheck()) { || path_end->isDataCheck()) {
const Clock *tgt_clk = path_end->targetClk(sta); const Clock *tgt_clk = path_end->targetClk(sta);
if (tgt_clk) if (tgt_clk)
group_name = tgt_clk->name(); return path_group_name == tgt_clk->name();
} }
else if (path_end->isGatedClock()) else if (path_end->isGatedClock())
group_name = gated_clk_group_name_; return path_group_name == gated_clk_group_name_;
else if (path_end->isPathDelay()) { else if (path_end->isPathDelay()) {
// Path delays that end at timing checks are part of the target clk group // Path delays that end at timing checks are part of the target clk group
// unless -ignore_clock_latency is true. // unless -ignore_clock_latency is true.
@ -517,13 +517,11 @@ PathGroups::pathGroupNames(const PathEnd *path_end,
const Clock *tgt_clk = path_end->targetClk(sta); const Clock *tgt_clk = path_end->targetClk(sta);
if (tgt_clk if (tgt_clk
&& !path_delay->ignoreClkLatency()) && !path_delay->ignoreClkLatency())
group_name = tgt_clk->name(); return path_group_name == tgt_clk->name();
else else
group_name = path_delay_group_name_; return path_group_name == path_delay_group_name_;
} }
if (!group_name.empty()) return false;
group_names.push_back(group_name);
return group_names;
} }
GroupPath * GroupPath *

View File

@ -33,6 +33,7 @@
#include "Graph.hh" #include "Graph.hh"
#include "Liberty.hh" #include "Liberty.hh"
#include "MinMax.hh" #include "MinMax.hh"
#include "Mode.hh"
#include "Network.hh" #include "Network.hh"
#include "Path.hh" #include "Path.hh"
#include "PathEnd.hh" #include "PathEnd.hh"
@ -1196,6 +1197,32 @@ Properties::getProperty(const Clock *clk,
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
PropertyValue
Properties::getProperty(const Scene *scene,
std::string_view property)
{
if (property == "name"
|| property == "full_name")
return PropertyValue(scene->name());
else
throw PropertyUnknown("scene", property);
}
////////////////////////////////////////////////////////////////
PropertyValue
Properties::getProperty(const Mode *mode,
std::string_view property)
{
if (property == "name"
|| property == "full_name")
return PropertyValue(mode->name());
else
throw PropertyUnknown("mode", property);
}
////////////////////////////////////////////////////////////////
PropertyValue PropertyValue
Properties::getProperty(PathEnd *end, Properties::getProperty(PathEnd *end,
std::string_view property) std::string_view property)

View File

@ -121,6 +121,22 @@ clock_property(Clock *clk,
return properties.getProperty(clk, property); return properties.getProperty(clk, property);
} }
PropertyValue
scene_property(Scene *scene,
const char *property)
{
Properties &properties = Sta::sta()->properties();
return properties.getProperty(scene, property);
}
PropertyValue
mode_property(Mode *mode,
const char *property)
{
Properties &properties = Sta::sta()->properties();
return properties.getProperty(mode, property);
}
PropertyValue PropertyValue
path_end_property(PathEnd *end, path_end_property(PathEnd *end,
const char *property) const char *property)

View File

@ -95,9 +95,9 @@ EvalPred::searchThru(Edge *edge,
{ {
const TimingRole *role = edge->role(); const TimingRole *role = edge->role();
return SearchPred0::searchThru(edge, mode) return SearchPred0::searchThru(edge, mode)
&& (sta_->variables()->dynamicLoopBreaking() || !edge->isDisabledLoop()) && (search_thru_latches_
&& (search_thru_latches_ || role->isLatchDtoQ() || role->isLatchDtoQ()
|| sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open); || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open);
} }
bool bool
@ -133,110 +133,115 @@ bool
SearchThru::searchThru(Edge *edge, SearchThru::searchThru(Edge *edge,
const Mode *mode) const 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 // SearchAdjLoop is mode independent. Search unless
// disabled to break combinational loop
// latch D->Q edge // latch D->Q edge
// timing check edge // timing check edge
// dynamic loop breaking pending tags // register set/clear
class SearchAdj : public SearchPred // bidirect load->driver
class SearchAdjLoop : public SearchPred
{ {
public: public:
SearchAdj(TagGroupBldr *tag_bldr, SearchAdjLoop(const StaState *sta);
const StaState *sta); bool searchFrom(const Vertex *from_vertex) const override;
bool searchFrom(const Vertex *from_vertex, bool searchFrom(const Vertex *from_vertex,
const Mode *mode) const override; const Mode *mode) const override;
bool searchThru(Edge *edge) const override;
bool searchThru(Edge *edge, bool searchThru(Edge *edge,
const Mode *mode) const override; const Mode *mode) const override;
bool searchTo(const Vertex *to_vertex) const override;
bool searchTo(const Vertex *to_vertex, bool searchTo(const Vertex *to_vertex,
const Mode *mode) const override; const Mode *mode) const override;
protected: protected:
bool loopEnabled(Edge *edge) const;
bool hasPendingLoopPaths(Edge *edge) const;
TagGroupBldr *tag_bldr_;
const StaState *sta_; const StaState *sta_;
}; };
SearchAdj::SearchAdj(TagGroupBldr *tag_bldr, SearchAdjLoop::SearchAdjLoop(const StaState *sta) :
const StaState *sta) :
SearchPred(sta), SearchPred(sta),
tag_bldr_(tag_bldr),
sta_(sta) sta_(sta)
{ {
} }
bool bool
SearchAdj::searchFrom(const Vertex * /* from_vertex */, SearchAdjLoop::searchFrom(const Vertex *) const
const Mode *) const
{ {
return true; return true;
} }
bool bool
SearchAdj::searchThru(Edge *edge, SearchAdjLoop::searchFrom(const Vertex *,
const Mode *) const const Mode *) const
{
return true;
}
bool
SearchAdjLoop::searchThru(Edge *edge) const
{ {
const TimingRole *role = edge->role(); const TimingRole *role = edge->role();
const Variables *variables = sta_->variables(); const Variables *variables = sta_->variables();
return !role->isTimingCheck() return !(role->isTimingCheck()
&& !role->isLatchDtoQ() || role->isLatchDtoQ()
// Register/latch preset/clr edges are disabled by default. // Register/latch preset/clr edges are disabled by default.
&& !(role == TimingRole::regSetClr() && !variables->presetClrArcsEnabled()) || (role == TimingRole::regSetClr()
&& !(edge->isBidirectInstPath() && !variables->bidirectInstPathsEnabled()) && !variables->presetClrArcsEnabled())
&& (!edge->isDisabledLoop() || sta_->isDisabledBidirectInstPath(edge));
|| (variables->dynamicLoopBreaking() && hasPendingLoopPaths(edge)));
} }
bool bool
SearchAdj::loopEnabled(Edge *edge) const SearchAdjLoop::searchThru(Edge *edge,
const Mode *) const
{ {
return !edge->isDisabledLoop() return searchThru(edge);
|| (sta_->variables()->dynamicLoopBreaking() && hasPendingLoopPaths(edge));
} }
bool bool
SearchAdj::hasPendingLoopPaths(Edge *edge) const SearchAdjLoop::searchTo(const Vertex *) 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
{ {
return true; 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) : Search::Search(StaState *sta) :
StaState(sta), StaState(sta),
search_thru_(new SearchThru(this)), search_thru_(new SearchThru(this)),
search_adj_(new SearchAdj(nullptr, search_adj_(new SearchAdj(this)),
this)),
eval_pred_(new EvalPred(this)), eval_pred_(new EvalPred(this)),
invalid_arrivals_(makeVertexSet(this)), invalid_arrivals_(makeVertexSet(this)),
@ -246,9 +251,7 @@ Search::Search(StaState *sta) :
arrival_visitor_(new ArrivalVisitor(this)), arrival_visitor_(new ArrivalVisitor(this)),
invalid_requireds_(makeVertexSet(this)), invalid_requireds_(makeVertexSet(this)),
required_iter_(new BfsBkwdIterator(BfsIndex::required, required_iter_(new BfsBkwdIterator(BfsIndex::required, search_adj_, this)),
search_adj_,
this)),
invalid_tns_(makeVertexSet(this)), invalid_tns_(makeVertexSet(this)),
clk_info_set_(new ClkInfoSet(ClkInfoLess(this))), clk_info_set_(new ClkInfoSet(ClkInfoLess(this))),
@ -260,8 +263,8 @@ Search::Search(StaState *sta) :
tag_group_capacity_(tag_capacity_), tag_group_capacity_(tag_capacity_),
tag_groups_(new TagGroup *[tag_group_capacity_]), tag_groups_(new TagGroup *[tag_group_capacity_]),
tag_group_set_(new TagGroupSet(tag_group_capacity_)), tag_group_set_(new TagGroupSet(tag_group_capacity_)),
pending_latch_outputs_(makeVertexSet(this)), postponed_arrivals_(makeVertexSet(this)),
pending_clk_endpoints_(makeVertexSet(this)), postponed_clk_endpoints_(makeVertexSet(this)),
endpoints_(makeVertexSet(this)), endpoints_(makeVertexSet(this)),
invalid_endpoints_(makeVertexSet(this)), invalid_endpoints_(makeVertexSet(this)),
@ -325,8 +328,8 @@ Search::clear()
deletePathGroups(); deletePathGroups();
deletePaths(); deletePaths();
deleteTags(); deleteTags();
clearPendingLatchOutputs(); postponed_arrivals_.clear();
pending_clk_endpoints_.clear(); postponed_clk_endpoints_.clear();
deleteFilter(); deleteFilter();
found_downstream_clk_pins_ = false; found_downstream_clk_pins_ = false;
} }
@ -643,18 +646,23 @@ Search::findFilteredArrivals(bool thru_latches)
// Search always_to_endpoint to search from exisiting arrivals at // Search always_to_endpoint to search from exisiting arrivals at
// fanin startpoints to reach -thru/-to endpoints. // fanin startpoints to reach -thru/-to endpoints.
arrival_visitor_->init(true, false, eval_pred_); arrival_visitor_->init(true, false, eval_pred_);
// Iterate until data arrivals at all latches stop changing.
postpone_latch_outputs_ = true;
enqueuePendingClkFanouts(); 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++) { pass++) {
if (thru_latches)
enqueuePendingLatchOutputs();
debugPrint(debug_, "search", 1, "find arrivals pass {}", pass); debugPrint(debug_, "search", 1, "find arrivals pass {}", pass);
int arrival_count = arrival_iter_->visitParallel(max_level, arrival_visitor_); int arrival_count = arrival_iter_->visitParallel(max_level, arrival_visitor_);
deleteTagsPrev();
debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count); 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; arrivals_exist_ = true;
} }
@ -982,57 +990,39 @@ Search::findAllArrivals(bool thru_latches,
if (!clks_only) if (!clks_only)
enqueuePendingClkFanouts(); enqueuePendingClkFanouts();
arrival_visitor_->init(false, clks_only, eval_pred_); arrival_visitor_->init(false, clks_only, eval_pred_);
bool have_pending_latch_outputs = false;
// Iterate until data arrivals at all latches stop changing. // Iterate until data arrivals at all latches stop changing.
postpone_latch_outputs_ = true; for (int pass = 1;
for (int pass = 1; pass == 1 || (thru_latches && havePendingLatchOutputs()); pass == 1 || (thru_latches && have_pending_latch_outputs);
pass++) { pass++) {
enqueuePendingLatchOutputs();
debugPrint(debug_, "search", 1, "find arrivals pass {}", pass); debugPrint(debug_, "search", 1, "find arrivals pass {}", pass);
findArrivals1(levelize_->maxLevel()); 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 // Pick up where the search stopped at the clock network boundary.
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();
}
void void
Search::enqueuePendingClkFanouts() Search::enqueuePendingClkFanouts()
{ {
for (Vertex *vertex : pending_clk_endpoints_) { for (Vertex *vertex : postponed_clk_endpoints_) {
debugPrint(debug_, "search", 2, "enqueue clk fanout {}", debugPrint(debug_, "search", 2, "enqueue clk fanout {}",
vertex->to_string(this)); vertex->to_string(this));
arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_); arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_);
} }
pending_clk_endpoints_.clear(); postponed_clk_endpoints_.clear();
} }
void void
Search::postponeClkFanouts(Vertex *vertex) Search::postponeClkFanouts(Vertex *vertex)
{ {
LockGuard lock(pending_clk_endpoints_lock_); LockGuard lock(postponed_clk_endpoints_lock_);
pending_clk_endpoints_.insert(vertex); postponed_clk_endpoints_.insert(vertex);
} }
void void
@ -1102,7 +1092,7 @@ ArrivalVisitor::init0()
{ {
tag_bldr_ = new TagGroupBldr(true, this); tag_bldr_ = new TagGroupBldr(true, this);
tag_bldr_no_crpr_ = new TagGroupBldr(false, this); tag_bldr_no_crpr_ = new TagGroupBldr(false, this);
adj_pred_ = new SearchAdj(tag_bldr_, this); search_adj_ = new SearchAdjLoop(this);
} }
void void
@ -1126,14 +1116,14 @@ void
ArrivalVisitor::copyState(const StaState *sta) ArrivalVisitor::copyState(const StaState *sta)
{ {
StaState::copyState(sta); StaState::copyState(sta);
adj_pred_->copyState(sta); search_adj_->copyState(sta);
} }
ArrivalVisitor::~ArrivalVisitor() ArrivalVisitor::~ArrivalVisitor()
{ {
delete tag_bldr_; delete tag_bldr_;
delete tag_bldr_no_crpr_; delete tag_bldr_no_crpr_;
delete adj_pred_; delete search_adj_;
} }
void void
@ -1144,16 +1134,27 @@ ArrivalVisitor::setAlwaysToEndpoints(bool to_endpoints)
void void
ArrivalVisitor::visit(Vertex *vertex) 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 {}", debugPrint(debug_, "search", 2, "find arrivals {}",
vertex->to_string(this)); vertex->to_string(this));
Pin *pin = vertex->pin(); Pin *pin = vertex->pin();
tag_bldr_->init(vertex); tag_bldr_->init(vertex);
has_fanin_one_ = graph_->hasFaninOne(vertex); has_fanin_one_ = graph_->hasFaninOne(vertex);
if (crpr_active_ && !has_fanin_one_) if (crpr_active_ && !has_fanin_one_)
tag_bldr_no_crpr_->init(vertex); tag_bldr_no_crpr_->init(vertex);
visitFaninPaths(vertex); visitFaninPaths(vertex, with_latch_edges);
if (crpr_active_ if (crpr_active_
&& search_->crprPathPruningEnabled() && search_->crprPathPruningEnabled()
// No crpr for ideal clocks. // No crpr for ideal clocks.
@ -1169,15 +1170,25 @@ ArrivalVisitor::visit(Vertex *vertex)
// previous eval pass enqueue the latch outputs to be re-evaled on the // previous eval pass enqueue the latch outputs to be re-evaled on the
// next pass. // next pass.
if (arrivals_changed && network_->isLatchData(pin)) if (arrivals_changed && network_->isLatchData(pin))
search_->enqueueLatchDataOutputs(vertex); search_->postponeLatchDataOutputs(vertex);
if ((always_to_endpoints_ || arrivals_changed)) { if ((always_to_endpoints_ || arrivals_changed)) {
if (clks_only_ && vertex->isRegClk()) { if (clks_only_ && vertex->isRegClk()) {
debugPrint(debug_, "search", 3, "postponing clk fanout"); debugPrint(debug_, "search", 3, "postponing clk fanout");
search_->postponeClkFanouts(vertex); search_->postponeClkFanouts(vertex);
} }
else else {
search_->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_); 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) { if (arrivals_changed) {
debugPrint(debug_, "search", 4, "arrivals changed"); debugPrint(debug_, "search", 4, "arrivals changed");
@ -1187,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 void
ArrivalVisitor::seedArrivals(Vertex *vertex) ArrivalVisitor::seedArrivals(Vertex *vertex)
{ {
@ -1226,7 +1257,6 @@ ArrivalVisitor::seedArrivals(Vertex *vertex)
network_->pathName(pin)); network_->pathName(pin));
search_->makeUnclkedPaths(vertex, true, false, tag_bldr_, mode); search_->makeUnclkedPaths(vertex, true, false, tag_bldr_, mode);
} }
enqueueRefPinInputDelays(pin, sdc);
} }
} }
@ -1387,46 +1417,25 @@ ArrivalVisitor::pruneCrprArrivals()
} }
} }
// Enqueue pins with input delays that use ref_pin as the clock
// reference pin as if there is a timing arc from the reference pin to
// the input delay pin.
void void
ArrivalVisitor::enqueueRefPinInputDelays(const Pin *ref_pin, Search::postponeLatchDataOutputs(Vertex *latch_data)
const Sdc *sdc)
{ {
InputDelaySet *input_delays = sdc->refPinInputDelays(ref_pin); VertexOutEdgeIterator edge_iter(latch_data, graph_);
if (input_delays) {
BfsFwdIterator *arrival_iter = search_->arrivalIterator();
for (InputDelay *input_delay : *input_delays) {
const Pin *pin = input_delay->pin();
Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
arrival_iter->enqueue(vertex);
if (bidirect_drvr_vertex)
arrival_iter->enqueue(bidirect_drvr_vertex);
}
}
}
void
Search::enqueueLatchDataOutputs(Vertex *vertex)
{
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) { while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next(); Edge *edge = edge_iter.next();
if (edge->role() == TimingRole::latchDtoQ()) { if (edge->role() == TimingRole::latchDtoQ()) {
Vertex *out_vertex = edge->to(graph_); Vertex *out_vertex = edge->to(graph_);
LockGuard lock(pending_latch_outputs_lock_); LockGuard lock(postponed_arrivals_lock_);
pending_latch_outputs_.insert(out_vertex); postponed_arrivals_.insert(out_vertex);
} }
} }
} }
void void
Search::enqueueLatchOutput(Vertex *vertex) Search::postponeArrivals(Vertex *vertex)
{ {
LockGuard lock(pending_latch_outputs_lock_); LockGuard lock(postponed_arrivals_lock_);
pending_latch_outputs_.insert(vertex); postponed_arrivals_.insert(vertex);
} }
void void
@ -1969,12 +1978,16 @@ PathVisitor::~PathVisitor()
} }
void void
PathVisitor::visitFaninPaths(Vertex *to_vertex) PathVisitor::visitFaninPaths(Vertex *to_vertex,
bool with_latch_edges)
{ {
VertexInEdgeIterator edge_iter(to_vertex, graph_); VertexInEdgeIterator edge_iter(to_vertex, graph_);
while (edge_iter.hasNext()) { while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next(); 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_); Vertex *from_vertex = edge->from(graph_);
const Pin *from_pin = from_vertex->pin(); const Pin *from_pin = from_vertex->pin();
const Pin *to_pin = to_vertex->pin(); const Pin *to_pin = to_vertex->pin();
@ -2015,7 +2028,8 @@ PathVisitor::visitEdge(const Pin *from_pin,
Path *from_path = from_iter.next(); Path *from_path = from_iter.next();
const Mode *mode = from_path->mode(this); const Mode *mode = from_path->mode(this);
if (mode == prev_mode 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))) { && pred_->searchTo(to_vertex, mode))) {
prev_mode = mode; prev_mode = mode;
const MinMax *min_max = from_path->minMax(this); const MinMax *min_max = from_path->minMax(this);
@ -2089,9 +2103,8 @@ PathVisitor::visitFromPath(const Pin *from_pin,
if (gclk) { if (gclk) {
Genclks *genclks = mode->genclks(); Genclks *genclks = mode->genclks();
VertexSet *fanins = genclks->fanins(gclk); VertexSet *fanins = genclks->fanins(gclk);
// Note: encountering a latch d->q edge means find the // Note: encountering a latch d->q edge means we need to find
// latch feedback edges, but they are referenced for // latch feedback edges.
// other edges in the gen clk fanout.
if (role == TimingRole::latchDtoQ()) if (role == TimingRole::latchDtoQ())
genclks->findLatchFdbkEdges(gclk); genclks->findLatchFdbkEdges(gclk);
EdgeSet &fdbk_edges = genclks->latchFdbkEdges(gclk); EdgeSet &fdbk_edges = genclks->latchFdbkEdges(gclk);
@ -2161,33 +2174,12 @@ PathVisitor::visitFromPath(const Pin *from_pin,
} }
else if (edge->role() == TimingRole::latchDtoQ()) { else if (edge->role() == TimingRole::latchDtoQ()) {
if (min_max == MinMax::max() && clk) { if (min_max == MinMax::max() && clk) {
bool postponed = false; arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max,
if (search_->postponeLatchOutputs()) { dcalc_ap, sdc);
const Path *from_clk_path = from_clk_info->crprClkPath(this); latches_->latchOutArrival(from_path, arc, edge, to_tag, arc_delay,
if (from_clk_path) { to_arrival);
Vertex *d_clk_vertex = from_clk_path->vertex(this); if (to_tag)
Level d_clk_level = d_clk_vertex->level(); to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_);
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_);
}
} }
} }
else if (from_tag->isClock()) { else if (from_tag->isClock()) {
@ -2754,7 +2746,8 @@ ReportPathLess::operator()(const Path *path1,
void void
Search::reportArrivals(Vertex *vertex, Search::reportArrivals(Vertex *vertex,
bool report_tag_index) const bool report_tag_index,
int digits) const
{ {
report_->report("Vertex {}", vertex->to_string(this)); report_->report("Vertex {}", vertex->to_string(this));
TagGroup *tag_group = tagGroup(vertex); TagGroup *tag_group = tagGroup(vertex);
@ -2771,7 +2764,6 @@ Search::reportArrivals(Vertex *vertex,
for (const Path *path : paths) { for (const Path *path : paths) {
const Tag *tag = path->tag(this); const Tag *tag = path->tag(this);
const RiseFall *rf = tag->transition(); const RiseFall *rf = tag->transition();
std::string req = delayAsString(path->required(), this);
bool report_prev = false; bool report_prev = false;
std::string prev_str; std::string prev_str;
if (report_prev) { if (report_prev) {
@ -2791,7 +2783,8 @@ Search::reportArrivals(Vertex *vertex,
} }
report_->report(" {} {} {} / {} {}{}", rf->shortName(), report_->report(" {} {} {} / {} {}{}", rf->shortName(),
path->minMax(this)->to_string(), path->minMax(this)->to_string(),
delayAsString(path->arrival(), this), req, delayAsString(path->arrival(), digits, this),
delayAsString(path->required(), digits, this),
tag->to_string(report_tag_index, false, this), prev_str); tag->to_string(report_tag_index, false, this), prev_str);
} }
} }
@ -3249,8 +3242,10 @@ Search::isEndpoint(Vertex *vertex,
const Pin *pin = vertex->pin(); const Pin *pin = vertex->pin();
const Sdc *sdc = mode->sdc(); const Sdc *sdc = mode->sdc();
return hasFanin(vertex, pred, graph_, mode) return hasFanin(vertex, pred, graph_, mode)
&& ((vertex->hasChecks() && hasEnabledChecks(vertex, mode)) && ((vertex->hasChecks()
|| sdc->isConstrainedEnd(pin) || !hasFanout(vertex, pred, graph_, mode) && hasEnabledChecks(vertex, mode))
|| sdc->isConstrainedEnd(pin)
|| !hasFanout(vertex, pred, graph_, mode)
|| sdc->isPathDelayInternalTo(pin) || sdc->isPathDelayInternalTo(pin)
// Unconstrained paths at register clk pins. // Unconstrained paths at register clk pins.
|| (unconstrained_paths_ && vertex->isRegClk()) || (unconstrained_paths_ && vertex->isRegClk())

View File

@ -72,6 +72,20 @@ private:
~PathEnd(); ~PathEnd();
}; };
class Scene
{
private:
Scene();
~Scene();
};
class Mode
{
private:
Mode();
~Mode();
};
%inline %{ %inline %{
using std::string; using std::string;
@ -269,9 +283,10 @@ report_tag_groups()
void void
report_tag_arrivals_cmd(Vertex *vertex, report_tag_arrivals_cmd(Vertex *vertex,
bool report_tag_index) bool report_tag_index,
int digits)
{ {
Sta::sta()->search()->reportArrivals(vertex, report_tag_index); Sta::sta()->search()->reportArrivals(vertex, report_tag_index, digits);
} }
void void

View File

@ -333,7 +333,7 @@ define_cmd_args "report_checks" \
[-sort_by_slack]\ [-sort_by_slack]\
[-path_group group_name]\ [-path_group group_name]\
[-format full|full_clock|full_clock_expanded|short|end|slack_only|summary|json]\ [-format full|full_clock|full_clock_expanded|short|end|slack_only|summary|json]\
[-fields capacitance|slew|fanout|input_pin|net|src_attr]\ [-fields capacitance|slew|fanout|input_pin|net|src_attr|variation]\
[-digits digits]\ [-digits digits]\
[-no_line_splits]\ [-no_line_splits]\
[> filename] [>> filename]} [> filename] [>> filename]}
@ -805,10 +805,19 @@ proc report_slack { args } {
################################################################ ################################################################
# Internal debugging command. # Internal debugging command.
proc report_tag_arrivals { pin } { proc report_tag_arrivals { args } {
set pin [get_port_pin_error "pin" $pin] global sta_report_default_digits
parse_key_args "report_tag_arrivals" args keys {-digits} flags {}
set pin [get_port_pin_error "pin" [lindex $args 0]]
if [info exists keys(-digits)] {
set digits $keys(-digits)
check_positive_integer "-digits" $digits
} else {
set digits $sta_report_default_digits
}
foreach vertex [$pin vertices] { foreach vertex [$pin vertices] {
report_tag_arrivals_cmd $vertex 1 report_tag_arrivals_cmd $vertex 1 $digits
} }
} }

View File

@ -184,6 +184,7 @@ ClkTreeSearchPred::searchThru(Edge *edge,
|| sdc->isDisabledConstraint(edge) || sdc->isDisabledConstraint(edge)
|| sdc->isDisabledCondDefault(edge) || sdc->isDisabledCondDefault(edge)
|| edge->isBidirectInstPath() || edge->isBidirectInstPath()
|| edge->isBidirectPortPath()
|| edge->isDisabledLoop()); || edge->isDisabledLoop());
} }

View File

@ -544,6 +544,8 @@ Sta::clearNonSdc()
for (Mode *mode : modes_) { for (Mode *mode : modes_) {
mode->clkNetwork()->clkPinsInvalid(); mode->clkNetwork()->clkPinsInvalid();
mode->sim()->clear(); mode->sim()->clear();
// ref_pin edges are owned by the graph deleted below; force a rebuild.
mode->sdc()->inputDelayRefPinEdgesInvalid();
} }
search_->clear(); search_->clear();
@ -805,7 +807,7 @@ Sta::setAnalysisType(AnalysisType analysis_type,
delaysInvalid(); delaysInvalid();
search_->deletePathGroups(); search_->deletePathGroups();
if (graph_) if (graph_)
graph_->setDelayCount(dcalcAnalysisPtCount()); graph_->delayCountChanged();
} }
} }
@ -1702,8 +1704,10 @@ Sta::disabledEdges(const Mode *mode)
VertexOutEdgeIterator edge_iter(vertex, graph_); VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) { while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next(); Edge *edge = edge_iter.next();
if (isDisabledConstant(edge, mode) || isDisabledCondDefault(edge) if (isDisabledConstant(edge, mode)
|| isDisabledConstraint(edge, sdc) || edge->isDisabledLoop() || isDisabledCondDefault(edge)
|| isDisabledConstraint(edge, sdc)
|| edge->isDisabledLoop()
|| isDisabledPresetClr(edge)) || isDisabledPresetClr(edge))
disabled_edges.push_back(edge); disabled_edges.push_back(edge);
} }
@ -1841,12 +1845,6 @@ Sta::exprConstantPins(FuncExpr *expr,
} }
} }
bool
Sta::isDisabledBidirectInstPath(Edge *edge) const
{
return !variables_->bidirectInstPathsEnabled() && edge->isBidirectInstPath();
}
bool bool
Sta::isDisabledPresetClr(Edge *edge) const Sta::isDisabledPresetClr(Edge *edge) const
{ {
@ -2311,6 +2309,8 @@ Sta::setPocvMode(PocvMode mode)
} }
updateComponentsState(); updateComponentsState();
delaysInvalid(); delaysInvalid();
if (graph_)
graph_->delayCountChanged();
} }
} }
@ -2554,7 +2554,7 @@ Sta::makeScenes(const StringSeq &scene_names)
cmd_scene_ = scenes_[0]; cmd_scene_ = scenes_[0];
updateComponentsState(); updateComponentsState();
if (graph_) if (graph_)
graph_->makeSceneAfter(); graph_->delayCountChanged();
} }
void void
@ -2583,7 +2583,7 @@ Sta::makeScene(const std::string &name,
Scene *scene = makeScene(name, mode, parasitics_min, parasitics_max); Scene *scene = makeScene(name, mode, parasitics_min, parasitics_max);
updateComponentsState(); updateComponentsState();
if (graph_) if (graph_)
graph_->makeSceneAfter(); graph_->delayCountChanged();
updateSceneLiberty(scene, liberty_min_files, liberty_max_files); updateSceneLiberty(scene, liberty_min_files, liberty_max_files);
cmd_scene_ = scene; cmd_scene_ = scene;
} }
@ -3304,15 +3304,11 @@ EndpointPathEndVisitor::copy() const
void void
EndpointPathEndVisitor::visit(PathEnd *path_end) EndpointPathEndVisitor::visit(PathEnd *path_end)
{ {
if (path_end->minMax(sta_) == min_max_) { if (path_end->minMax(sta_) == min_max_
StringSeq group_names = PathGroups::pathGroupNames(path_end, sta_); && PathGroups::inPathGroupNamed(path_end, path_group_name_, sta_)) {
for (std::string &group_name : group_names) { Slack end_slack = path_end->slack(sta_);
if (group_name == path_group_name_) { if (delayLess(end_slack, slack_, sta_))
Slack end_slack = path_end->slack(sta_); slack_ = end_slack;
if (delayLess(end_slack, slack_, sta_))
slack_ = end_slack;
}
}
} }
} }
@ -3900,7 +3896,6 @@ Sta::setAnnotatedSlew(Vertex *vertex,
const RiseFallBoth *rf, const RiseFallBoth *rf,
float slew) float slew)
{ {
ensureGraph();
for (const MinMax *mm : min_max->range()) { for (const MinMax *mm : min_max->range()) {
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(mm); DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(mm);
for (const RiseFall *rf1 : rf->range()) { for (const RiseFall *rf1 : rf->range()) {
@ -3912,6 +3907,24 @@ Sta::setAnnotatedSlew(Vertex *vertex,
graph_delay_calc_->delayInvalid(vertex); graph_delay_calc_->delayInvalid(vertex);
} }
void
Sta::unsetAnnotatedSlew(Vertex *vertex,
const Scene *scene,
const MinMaxAll *min_max,
const RiseFallBoth *rf)
{
for (const MinMax *mm : min_max->range()) {
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(mm);
for (const RiseFall *rf1 : rf->range()) {
vertex->setSlewAnnotated(false, rf1, ap_index);
}
}
if (vertex->isDriver(network_))
graph_delay_calc_->delayInvalid(vertex);
else
delaysInvalidFromFanin(vertex);
}
void void
Sta::writeSdf(std::string_view filename, Sta::writeSdf(std::string_view filename,
const Scene *scene, const Scene *scene,
@ -4316,8 +4329,15 @@ Parasitics *
Sta::makeConcreteParasitics(std::string_view name, Sta::makeConcreteParasitics(std::string_view name,
std::string_view filename) std::string_view filename)
{ {
// Free the prior entry to avoid leaking it on overwrite.
std::string key(name);
auto it = parasitics_name_map_.find(key);
if (it != parasitics_name_map_.end()) {
delete it->second;
parasitics_name_map_.erase(it);
}
Parasitics *parasitics = new ConcreteParasitics(name, filename, this); Parasitics *parasitics = new ConcreteParasitics(name, filename, this);
parasitics_name_map_[std::string(name)] = parasitics; parasitics_name_map_[key] = parasitics;
return parasitics; return parasitics;
} }
@ -4413,9 +4433,16 @@ Sta::makeNet(const char *name,
Instance *parent) Instance *parent)
{ {
NetworkEdit *network = networkCmdEdit(); NetworkEdit *network = networkCmdEdit();
Net *net = network->makeNet(name, parent); std::string escaped = escapeBrackets(name, network);
// Sta notification unnecessary. if (network->findNet(parent, escaped)) {
return net; report_->warn(1557, "net {} already exists.", name);
return nullptr;
}
else {
Net *net = network->makeNet(escaped, parent);
// Sta notification unnecessary.
return net;
}
} }
void void
@ -4461,8 +4488,9 @@ Sta::makePortPin(const char *port_name,
ensureLinked(); ensureLinked();
NetworkReader *network = dynamic_cast<NetworkReader *>(network_); NetworkReader *network = dynamic_cast<NetworkReader *>(network_);
Instance *top_inst = network->topInstance(); Instance *top_inst = network->topInstance();
std::string escaped = escapeBrackets(port_name, network);
Cell *top_cell = network->cell(top_inst); Cell *top_cell = network->cell(top_inst);
Port *port = network->makePort(top_cell, port_name); Port *port = network->makePort(top_cell, escaped);
network->setDirection(port, dir); network->setDirection(port, dir);
Pin *pin = network->makePin(top_inst, port, nullptr); Pin *pin = network->makePin(top_inst, port, nullptr);
makePortPinAfter(pin); makePortPinAfter(pin);
@ -5075,9 +5103,11 @@ Sta::delaysInvalidFromFanin(Vertex *vertex)
VertexInEdgeIterator edge_iter(vertex, graph_); VertexInEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) { while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next(); Edge *edge = edge_iter.next();
Vertex *from_vertex = edge->from(graph_); if (edge->isWire()) {
delaysInvalidFrom(from_vertex); Vertex *from_vertex = edge->from(graph_);
search_->requiredInvalid(from_vertex); delaysInvalidFrom(from_vertex);
search_->requiredInvalid(from_vertex);
}
} }
} }
@ -5218,8 +5248,10 @@ FanInOutSrchPred::searchThru(Edge *edge,
const Sim *sim = mode->sim(); const Sim *sim = mode->sim();
return searchThruRole(edge) return searchThruRole(edge)
&& (thru_disabled_ && (thru_disabled_
|| !(sdc->isDisabledConstraint(edge) || sim->isDisabledCond(edge) || !(sdc->isDisabledConstraint(edge)
|| sta_->isDisabledCondDefault(edge))) || sim->isDisabledCond(edge)
|| sta_->isDisabledCondDefault(edge)
|| sta_->isDisabledBidirectInstPath(edge)))
&& (thru_constants_ || sim->simTimingSense(edge) != TimingSense::none); && (thru_constants_ || sim->simTimingSense(edge) != TimingSense::none);
} }

View File

@ -31,6 +31,7 @@
#include "Graph.hh" #include "Graph.hh"
#include "Mode.hh" #include "Mode.hh"
#include "Network.hh" #include "Network.hh"
#include "PortDirection.hh"
#include "Scene.hh" #include "Scene.hh"
#include "Sdc.hh" #include "Sdc.hh"
#include "TimingArc.hh" #include "TimingArc.hh"
@ -124,6 +125,14 @@ StaState::isDisabledCondDefault(const Edge *edge) const
&& edge->timingArcSet()->isCondDefault(); && edge->timingArcSet()->isCondDefault();
} }
bool
StaState::isDisabledBidirectInstPath(Edge *edge) const
{
return (edge->isBidirectInstPath()
|| edge->isBidirectPortPath())
&& !variables_->bidirectInstPathsEnabled();
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
size_t size_t

View File

@ -1838,7 +1838,7 @@ TEST_F(StaDesignTest, SearchReportArrivals) {
Search *search = sta_->search(); Search *search = sta_->search();
Vertex *v = findVertex("r1/Q"); Vertex *v = findVertex("r1/Q");
ASSERT_NE(v, nullptr); ASSERT_NE(v, nullptr);
search->reportArrivals(v, false); search->reportArrivals(v, false, 3);
} }
// --- Search: reportPathCountHistogram --- // --- Search: reportPathCountHistogram ---
@ -4143,8 +4143,8 @@ TEST_F(StaDesignTest, SearchReportArrivals2) {
Search *search = sta_->search(); Search *search = sta_->search();
Vertex *v = findVertex("r1/Q"); Vertex *v = findVertex("r1/Q");
ASSERT_NE(v, nullptr); ASSERT_NE(v, nullptr);
search->reportArrivals(v, false); search->reportArrivals(v, false, 3);
search->reportArrivals(v, true); search->reportArrivals(v, true, 3);
} }
TEST_F(StaDesignTest, SearchSeedArrival) { TEST_F(StaDesignTest, SearchSeedArrival) {

View File

@ -14,7 +14,7 @@ report_checks > /dev/null
puts "--- Corner commands ---" puts "--- Corner commands ---"
set corner [sta::cmd_scene] set corner [sta::cmd_scene]
puts "Corner name: $corner" puts "Corner name: [get_name $corner]"
puts "Multi corner: [sta::multi_scene]" puts "Multi corner: [sta::multi_scene]"
puts "--- ClkSkew report with propagated clock ---" puts "--- ClkSkew report with propagated clock ---"

View File

@ -97,8 +97,8 @@ puts "paths: [sta::path_count]"
puts "--- report_tag_arrivals ---" puts "--- report_tag_arrivals ---"
set v [sta::worst_slack_vertex max] set v [sta::worst_slack_vertex max]
if { $v != "NULL" } { if { $v != "NULL" } {
sta::report_tag_arrivals_cmd $v 1 sta::report_tag_arrivals_cmd $v 1 3
sta::report_tag_arrivals_cmd $v 0 sta::report_tag_arrivals_cmd $v 0 3
} }
############################################################ ############################################################

View File

@ -86,8 +86,8 @@ if { $wv != "NULL" } {
} }
# report_tag_arrivals # report_tag_arrivals
sta::report_tag_arrivals_cmd $wv 1 sta::report_tag_arrivals_cmd $wv 1 3
sta::report_tag_arrivals_cmd $wv 0 sta::report_tag_arrivals_cmd $wv 0 3
} }
puts "--- worst_slack_vertex min ---" puts "--- worst_slack_vertex min ---"

View File

@ -536,14 +536,23 @@ proc parse_scenes_or_all { keys_var } {
} }
} }
proc find_scenes { scene_names } { proc find_scenes { scenes_arg } {
set scenes {} set scenes {}
foreach scene_name $scene_names { foreach scene_arg $scenes_arg {
set scene [find_scene $scene_name] if { [is_object $scene_arg] } {
if { $scene == "NULL" } { set object_type [object_type $scene_arg]
sta_error 134 "$scene_name is not the name of a scene." if { $object_type == "Scene" } {
lappend scenes $scene_arg
} else {
sta_error 135 "scene object type '$object_type' is not a scene."
}
} else { } else {
lappend scenes $scene set scene [find_scene $scene_arg]
if { $scene == "NULL" } {
sta_error 134 "$scene_arg is not the name of a scene."
} else {
lappend scenes $scene
}
} }
} }
return $scenes return $scenes

View File

@ -57,6 +57,10 @@ proc get_object_property { object prop } {
return [net_property $object $prop] return [net_property $object $prop]
} elseif { $object_type == "Clock" } { } elseif { $object_type == "Clock" } {
return [clock_property $object $prop] return [clock_property $object $prop]
} elseif { $object_type == "Scene" } {
return [scene_property $object $prop]
} elseif { $object_type == "Mode" } {
return [mode_property $object $prop]
} elseif { $object_type == "Port" } { } elseif { $object_type == "Port" } {
return [port_property $object $prop] return [port_property $object $prop]
} elseif { $object_type == "LibertyPort" } { } elseif { $object_type == "LibertyPort" } {

View File

@ -102,7 +102,12 @@ define_cmd_args "set_scene" {scene_name}
proc set_scene { args } { proc set_scene { args } {
check_argc_eq1 "set_scene" $args check_argc_eq1 "set_scene" $args
set_cmd_scene [lindex $args 0] set scene_name [lindex $args 0]
set scene [find_scene $scene_name]
if { $scene == "NULL" } {
sta_error 578 "$scene_name is not the name of a scene."
}
set_cmd_scene $scene
} }
################################################################ ################################################################
@ -118,10 +123,16 @@ proc get_scenes { args } {
} else { } else {
set scene_name [lindex $args 0] set scene_name [lindex $args 0]
} }
set mode_names {}
if { [info exists keys(-modes)] } { if { [info exists keys(-modes)] } {
set mode_names $keys(-modes) set modes {}
return [find_mode_scenes_matching $scene_name $mode_names] foreach mode_name $keys(-modes) {
set mode [find_mode $mode_name]
if { $mode == "NULL" } {
sta_error 579 "$mode_name is not the name of a mode."
}
lappend modes $mode
}
return [find_mode_scenes_matching $scene_name $modes]
} else { } else {
return [find_scenes_matching $scene_name] return [find_scenes_matching $scene_name]
} }

View File

@ -1189,106 +1189,70 @@ using namespace sta;
$1 = tclListSetConstChar($input, interp); $1 = tclListSetConstChar($input, interp);
} }
%typemap(in) Mode* {
Tcl_Size length;
const char *arg = Tcl_GetStringFromObj($input, &length);
if (stringEqual(arg, "NULL"))
$1 = nullptr;
else {
void *obj;
if (SWIG_ConvertPtr($input, &obj, SWIGTYPE_p_Mode, false) != TCL_OK) {
tclArgError(interp, 2174, "{} is not a mode object.", arg);
return TCL_ERROR;
}
$1 = reinterpret_cast<Mode*>(obj);
}
}
%typemap(out) Mode* { %typemap(out) Mode* {
const Mode *mode = $1; Mode *mode = $1;
if (mode) if (mode) {
Tcl_SetResult(interp, const_cast<char*>($1->name().c_str()), TCL_VOLATILE); Tcl_Obj *obj = SWIG_NewInstanceObj(mode, SWIGTYPE_p_Mode, false);
Tcl_SetObjResult(interp, obj);
}
else else
Tcl_SetResult(interp, const_cast<char*>("NULL"), TCL_STATIC); Tcl_SetResult(interp, const_cast<char*>("NULL"), TCL_STATIC);
} }
%typemap(in) ModeSeq { %typemap(in) ModeSeq {
Tcl_Size argc; $1 = tclListSeq<Mode*>($input, SWIGTYPE_p_Mode, interp);
Tcl_Obj **argv;
Sta *sta = Sta::sta();
std::vector<Mode*> seq;
if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK
&& argc > 0) {
for (int i = 0; i < argc; i++) {
Tcl_Size length;
const char *mode_name = Tcl_GetStringFromObj(argv[i], &length);
Mode *mode = sta->findMode(mode_name);
if (mode)
seq.push_back(mode);
else {
tclArgError(interp, 2174, "mode {} not found.", mode_name);
return TCL_ERROR;
}
}
}
$1 = seq;
} }
%typemap(out) ModeSeq { %typemap(out) ModeSeq {
Tcl_Obj *list = Tcl_NewListObj(0, nullptr); seqTclList<ModeSeq, Mode>($1, SWIGTYPE_p_Mode, interp);
ModeSeq &modes = $1;
for (Mode *mode : modes) {
const std::string &mode_name = mode->name();
Tcl_Obj *obj = Tcl_NewStringObj(mode_name.c_str(), mode_name.size());
Tcl_ListObjAppendElement(interp, list, obj);
}
Tcl_SetObjResult(interp, list);
} }
%typemap(in) Scene* { %typemap(in) Scene* {
sta::Sta *sta = Sta::sta();
Tcl_Size length; Tcl_Size length;
std::string scene_name = Tcl_GetStringFromObj($input, &length); const char *arg = Tcl_GetStringFromObj($input, &length);
// parse_scene_or_all support depreated 11/21/2025 if (stringEqual(arg, "NULL"))
if (scene_name == "NULL")
$1 = nullptr; $1 = nullptr;
else { else {
Scene *scene = sta->findScene(scene_name); void *obj;
if (scene) if (SWIG_ConvertPtr($input, &obj, SWIGTYPE_p_Scene, false) != TCL_OK) {
$1 = scene; tclArgError(interp, 2173, "{} is not a scene object.", arg);
else {
tclArgError(interp, 2173, "scene {} not found.", scene_name.c_str());
return TCL_ERROR; return TCL_ERROR;
} }
$1 = reinterpret_cast<Scene*>(obj);
} }
} }
%typemap(out) Scene* { %typemap(out) Scene* {
const Scene *scene = $1; Scene *scene = $1;
if (scene) if (scene) {
Tcl_SetResult(interp, const_cast<char*>($1->name().c_str()), TCL_VOLATILE); Tcl_Obj *obj = SWIG_NewInstanceObj(scene, SWIGTYPE_p_Scene, false);
Tcl_SetObjResult(interp, obj);
}
else else
Tcl_SetResult(interp, const_cast<char*>("NULL"), TCL_STATIC); Tcl_SetResult(interp, const_cast<char*>("NULL"), TCL_STATIC);
} }
%typemap(in) SceneSeq { %typemap(in) SceneSeq {
Tcl_Size argc; $1 = tclListSeq<Scene*>($input, SWIGTYPE_p_Scene, interp);
Tcl_Obj **argv;
Sta *sta = Sta::sta();
std::vector<Scene*> seq;
if (Tcl_ListObjGetElements(interp, $input, &argc, &argv) == TCL_OK
&& argc > 0) {
for (int i = 0; i < argc; i++) {
Tcl_Size length;
const char *scene_name = Tcl_GetStringFromObj(argv[i], &length);
Scene *scene = sta->findScene(scene_name);
if (scene)
seq.push_back(scene);
else {
tclArgError(interp, 2172, "scene {} not found.", scene_name);
return TCL_ERROR;
}
}
}
$1 = seq;
} }
%typemap(out) SceneSeq { %typemap(out) SceneSeq {
Tcl_Obj *list = Tcl_NewListObj(0, nullptr); seqTclList<SceneSeq, Scene>($1, SWIGTYPE_p_Scene, interp);
SceneSeq &scenes = $1;
for (Scene *scene : scenes) {
const std::string &scene_name = scene->name();
Tcl_Obj *obj = Tcl_NewStringObj(scene_name.c_str(), scene_name.size());
Tcl_ListObjAppendElement(interp, list, obj);
}
Tcl_SetObjResult(interp, list);
} }
%typemap(in) PropertyValue { %typemap(in) PropertyValue {

6
test/get_scenes.ok Normal file
View File

@ -0,0 +1,6 @@
[get_scenes *]
scene1
scene2
[get_modes *]
mode1
mode2

20
test/get_scenes.tcl Normal file
View File

@ -0,0 +1,20 @@
# get_scenes / get_modes return objects
read_liberty ../examples/asap7_small_ff.lib.gz
read_liberty ../examples/asap7_small_ss.lib.gz
read_verilog ../examples/reg1_asap7.v
link_design top
read_sdc -mode mode1 ../examples/mcmm2_mode1.sdc
read_sdc -mode mode2 ../examples/mcmm2_mode2.sdc
read_spef -name reg1_ff ../examples/reg1_asap7.spef
read_spef -name reg1_ss ../examples/reg1_asap7_ss.spef
define_scene scene1 -mode mode1 -liberty asap7_small_ff -spef reg1_ff
define_scene scene2 -mode mode2 -liberty asap7_small_ss -spef reg1_ss
puts {[get_scenes *]}
report_object_names [get_scenes *]
puts {[get_modes *]}
report_object_names [get_modes *]

View File

@ -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)

View File

@ -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

View File

View File

@ -0,0 +1,9 @@
# Sta::makeConcreteParasitics map-overwrite leak repro.
# Run under -fsanitize=address to verify no leak after the fix.
read_liberty asap7_small.lib.gz
read_verilog reg1_asap7.v
link_design top
# 1st read_spef
read_spef -min reg1_asap7.spef
# 2nd read_spef
read_spef -min reg1_asap7.spef

View File

@ -147,13 +147,16 @@ record_public_tests {
get_is_memory get_is_memory
get_lib_pins_of_objects get_lib_pins_of_objects
get_noargs get_noargs
get_scenes
get_objrefs get_objrefs
input_delay_ref_pin_rebuild
liberty_arcs_one2one_1 liberty_arcs_one2one_1
liberty_arcs_one2one_2 liberty_arcs_one2one_2
liberty_backslash_eol liberty_backslash_eol
liberty_ccsn liberty_ccsn
liberty_float_as_str liberty_float_as_str
liberty_latch3 liberty_latch3
make_concrete_parasitics_leak
package_require package_require
path_group_names path_group_names
power_json power_json
@ -168,6 +171,7 @@ record_public_tests {
verilog_well_supplies verilog_well_supplies
verilog_specify verilog_specify
verilog_write_escape verilog_write_escape
verilog_write_gzip
verilog_unconnected_hpin verilog_unconnected_hpin
} }

View File

@ -0,0 +1,33 @@
module top (in1,
in2,
clk1,
clk2,
clk3,
out);
input in1;
input in2;
input clk1;
input clk2;
input clk3;
output out;
wire r1q;
wire r2q;
wire u1z;
wire u2z;
DFFHQx4_ASAP7_75t_R r1 (.Q(r1q),
.CLK(clk1),
.D(in1));
DFFHQx4_ASAP7_75t_R r2 (.Q(r2q),
.CLK(clk2),
.D(in2));
DFFHQx4_ASAP7_75t_R r3 (.Q(out),
.CLK(clk3),
.D(u2z));
BUFx2_ASAP7_75t_R u1 (.Y(u1z),
.A(r2q));
AND2x2_ASAP7_75t_R u2 (.Y(u2z),
.A(r1q),
.B(u1z));
endmodule

View File

@ -0,0 +1,8 @@
# Check that write_verilog supports gzip compression natively if .gz extension is provided.
source helpers.tcl
read_liberty asap7_small.lib.gz
read_verilog reg1_asap7.v
link_design top
set verilog_file [make_result_file "verilog_write_gzip.v.gz"]
write_verilog $verilog_file
report_file $verilog_file

View File

@ -1,15 +1,16 @@
// Author Phillip Johnston // Original Author: Phillip Johnston
// Licensed under CC0 1.0 Universal // Licensed under CC0 1.0 Universal
// https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp // Original source: https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp
// https://embeddedartistry.com/blog/2017/2/1/dispatch-queues?rq=dispatch // Original article: https://embeddedartistry.com/blog/2017/2/1/dispatch-queues?rq=dispatch
//
// Modified for OpenSTA to use C++20 non-spinning DynamicLatch for synchronization.
#include "DispatchQueue.hh" #include "DispatchQueue.hh"
namespace sta { namespace sta {
DispatchQueue::DispatchQueue(size_t thread_count) : DispatchQueue::DispatchQueue(size_t thread_count) :
threads_(thread_count), threads_(thread_count)
pending_task_count_(0)
{ {
for(size_t i = 0; i < thread_count; i++) for(size_t i = 0; i < thread_count; i++)
threads_[i] = std::thread(&DispatchQueue::dispatch_thread_handler, this, i); threads_[i] = std::thread(&DispatchQueue::dispatch_thread_handler, this, i);
@ -58,8 +59,7 @@ DispatchQueue::getThreadCount() const
void void
DispatchQueue::finishTasks() DispatchQueue::finishTasks()
{ {
while (pending_task_count_.load(std::memory_order_acquire) != 0) pending_task_count_latch_.wait();
std::this_thread::yield();
} }
void void
@ -67,7 +67,7 @@ DispatchQueue::dispatch(const fp_t& op)
{ {
std::unique_lock<std::mutex> lock(lock_); std::unique_lock<std::mutex> lock(lock_);
q_.push(op); q_.push(op);
pending_task_count_++; pending_task_count_latch_.countUp();
// Manual unlocking is done before notifying, to avoid waking up // Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details) // the waiting thread only to block again (see notify_one for details)
@ -80,7 +80,7 @@ DispatchQueue::dispatch(fp_t&& op)
{ {
std::unique_lock<std::mutex> lock(lock_); std::unique_lock<std::mutex> lock(lock_);
q_.push(std::move(op)); q_.push(std::move(op));
pending_task_count_++; pending_task_count_latch_.countUp();
// Manual unlocking is done before notifying, to avoid waking up // Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details) // the waiting thread only to block again (see notify_one for details)
@ -106,7 +106,7 @@ DispatchQueue::dispatch_thread_handler(size_t i)
op(i); op(i);
pending_task_count_--; pending_task_count_latch_.countDown();
lock.lock(); lock.lock();
} }
} while (!quit_); } while (!quit_);

View File

@ -242,13 +242,8 @@ stmt_seq:
continuous_assign continuous_assign
; ;
/* specify blocks are used by some comercial tools to convey macro timing // Specify blocks are used by some comercial tools to convey macro timing
* and other metadata. // and other metadata.
* Their presence is not forbidden in structural verilog, this is a placeholder
* that just ignores them and allows verilog processing to proceed
* <<TODO>> if someone in the future wants implement support for timing info
* via specify blocks, implement proper parsing here
*/
specify_block: specify_block:
SPECIFY specify_stmts ENDSPECIFY SPECIFY specify_stmts ENDSPECIFY
{ $$ = nullptr; } { $$ = nullptr; }

View File

@ -1468,8 +1468,7 @@ VerilogReader::linkNetwork(std::string_view top_cell_name,
while (net_name_iter->hasNext()) { while (net_name_iter->hasNext()) {
const std::string &net_name = net_name_iter->next(); const std::string &net_name = net_name_iter->next();
Port *port = network_->findPort(top_cell, net_name); Port *port = network_->findPort(top_cell, net_name);
Net *net = Net *net = bindings.ensureNetBinding(net_name, top_instance, network_);
bindings.ensureNetBinding(net_name, top_instance, network_);
// Guard against repeated port name. // Guard against repeated port name.
if (network_->findPin(top_instance, port) == nullptr) { if (network_->findPin(top_instance, port) == nullptr) {
Pin *pin = network_->makePin(top_instance, port, nullptr); Pin *pin = network_->makePin(top_instance, port, nullptr);
@ -1521,13 +1520,11 @@ VerilogReader::makeModuleInstBody(VerilogModule *module,
if (assign) if (assign)
mergeAssignNet(assign, module, inst, bindings); mergeAssignNet(assign, module, inst, bindings);
if (dir->isGround()) { if (dir->isGround()) {
Net *net = Net *net = bindings->ensureNetBinding(arg->netName(), inst, network_);
bindings->ensureNetBinding(arg->netName(), inst, network_);
network_->addConstantNet(net, LogicValue::zero); network_->addConstantNet(net, LogicValue::zero);
} }
if (dir->isPower()) { if (dir->isPower()) {
Net *net = Net *net = bindings->ensureNetBinding(arg->netName(), inst, network_);
bindings->ensureNetBinding(arg->netName(), inst, network_);
network_->addConstantNet(net, LogicValue::one); network_->addConstantNet(net, LogicValue::one);
} }
} }

View File

@ -33,6 +33,7 @@
#include "Error.hh" #include "Error.hh"
#include "Format.hh" #include "Format.hh"
#include "Liberty.hh" #include "Liberty.hh"
#include "Zlib.hh"
#include "Network.hh" #include "Network.hh"
#include "NetworkCmp.hh" #include "NetworkCmp.hh"
#include "ParseBus.hh" #include "ParseBus.hh"
@ -47,7 +48,7 @@ public:
VerilogWriter(const char *filename, VerilogWriter(const char *filename,
bool include_pwr_gnd, bool include_pwr_gnd,
CellSeq *remove_cells, CellSeq *remove_cells,
FILE *stream, gzFile stream,
Network *network); Network *network);
void writeModules(); void writeModules();
@ -82,7 +83,7 @@ protected:
const char *filename_; const char *filename_;
bool include_pwr_gnd_; bool include_pwr_gnd_;
CellSet remove_cells_; CellSet remove_cells_;
FILE *stream_; gzFile stream_;
Network *network_; Network *network_;
int unconnected_net_index_{1}; int unconnected_net_index_{1};
}; };
@ -94,12 +95,13 @@ writeVerilog(const char *filename,
Network *network) Network *network)
{ {
if (network->topInstance()) { if (network->topInstance()) {
FILE *stream = fopen(filename, "w"); bool gzip = std::string_view(filename).ends_with(".gz");
gzFile stream = gzopen(filename, gzip ? "wb" : "wT");
if (stream) { if (stream) {
VerilogWriter writer(filename, include_pwr_gnd, VerilogWriter writer(filename, include_pwr_gnd,
remove_cells, stream, network); remove_cells, stream, network);
writer.writeModules(); writer.writeModules();
fclose(stream); gzclose(stream);
} }
else else
throw FileNotWritable(filename); throw FileNotWritable(filename);
@ -109,7 +111,7 @@ writeVerilog(const char *filename,
VerilogWriter::VerilogWriter(const char *filename, VerilogWriter::VerilogWriter(const char *filename,
bool include_pwr_gnd, bool include_pwr_gnd,
CellSeq *remove_cells, CellSeq *remove_cells,
FILE *stream, gzFile stream,
Network *network) : Network *network) :
filename_(filename), filename_(filename),
include_pwr_gnd_(include_pwr_gnd), include_pwr_gnd_(include_pwr_gnd),