Merge branch 'master' into k8s-pipeline-opensta
This commit is contained in:
commit
34cf0af98e
|
|
@ -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`
|
||||||
|
|
|
||||||
|
|
@ -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 }}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
8
BUILD
|
|
@ -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
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
18
README.md
18
README.md
|
|
@ -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 .
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
346
dcalc/DmpCeff.cc
346
dcalc/DmpCeff.cc
|
|
@ -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) {
|
||||||
// Find implicit scaling factors.
|
double det = fjac.topLeftCorner<2, 2>().determinant();
|
||||||
for (int i = 0; i < size; i++) {
|
if (std::abs(det) < 1e-12) {
|
||||||
double big = 0.0;
|
throw DmpError("Jacobian is singular (order 2)");
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
p.head<2>() = fjac.topLeftCorner<2, 2>().inverse() * -fvec.head<2>();
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solves fjac_ * x = p_ for x, assuming fjac_ is LU form from luDecomp.
|
double det = fjac.determinant();
|
||||||
// Solution overwrites p_.
|
if (std::abs(det) < 1e-12) {
|
||||||
void
|
throw DmpError("Jacobian is singular (order 3)");
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,7 +338,6 @@ 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);
|
||||||
|
|
@ -373,7 +371,6 @@ GraphDelayCalc::findDelays(Level level)
|
||||||
debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count);
|
debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count);
|
||||||
stats.report("Delay calc");
|
stats.report("Delay calc");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
GraphDelayCalc::seedInvalidDelays()
|
GraphDelayCalc::seedInvalidDelays()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
2621
doc/OpenSTA.fodt
2621
doc/OpenSTA.fodt
File diff suppressed because it is too large
Load Diff
BIN
doc/OpenSTA.pdf
BIN
doc/OpenSTA.pdf
Binary file not shown.
145
graph/Graph.cc
145
graph/Graph.cc
|
|
@ -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,18 +853,14 @@ 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
|
||||||
Graph::initSlews()
|
Graph::initSlews()
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,7 +190,6 @@ public:
|
||||||
// Remove all delay and slew annotations.
|
// Remove all delay and slew annotations.
|
||||||
void removeDelaySlewAnnotations();
|
void removeDelaySlewAnnotations();
|
||||||
VertexSet ®ClkVertices() { return reg_clk_vertices_; }
|
VertexSet ®ClkVertices() { 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;
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,8 @@ 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,
|
||||||
|
std::string_view path_group_name,
|
||||||
const StaState *sta);
|
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_; }
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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_; }
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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_
|
||||||
|
|| duty_delta > change_tolerance_
|
||||||
|| activity.origin() != prev_activity.origin();
|
|| 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,9 +1590,13 @@ 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);
|
||||||
|
if (clk) {
|
||||||
float duty = clockDuty(clk);
|
float duty = clockDuty(clk);
|
||||||
return PwrActivity(2.0 / clk->period(), duty, PwrActivityOrigin::clock);
|
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_;
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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++;
|
||||||
|
|
|
||||||
|
|
@ -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) :
|
||||||
|
|
|
||||||
66
sdc/Sdc.cc
66
sdc/Sdc.cc
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,11 +269,18 @@ 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_);
|
||||||
|
for (Vertex *vertex : src_vertices)
|
||||||
|
master_queue.push(vertex);
|
||||||
|
while (!master_queue.empty()) {
|
||||||
|
Vertex *vertex = master_queue.front();
|
||||||
|
master_queue.pop();
|
||||||
|
if (!visited.contains(vertex)) {
|
||||||
|
visited.insert(vertex);
|
||||||
Pin *pin = vertex->pin();
|
Pin *pin = vertex->pin();
|
||||||
if (sdc->isLeafPinClock(pin)) {
|
if (sdc->isLeafPinClock(pin)) {
|
||||||
ClockSet *master_clks = sdc->findLeafPinClocks(pin);
|
ClockSet *master_clks = sdc->findLeafPinClocks(pin);
|
||||||
|
|
@ -293,7 +299,8 @@ Genclks::ensureMaster(Clock *gclk,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iter.enqueueAdjacentVertices(vertex, mode_);
|
enqueueFanin(vertex, master_queue, srch_pred);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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,11 +613,33 @@ Genclks::seedSrcPins(Clock *gclk,
|
||||||
tag_bldr.setArrival(tag, insert);
|
tag_bldr.setArrival(tag, insert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
search_->setVertexArrivals(vertex, &tag_bldr);
|
search_->setVertexArrivals(vertex, &tag_bldr);
|
||||||
insert_iter.enqueueAdjacentVertices(vertex, mode_);
|
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 *
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|| edge->isDisabledLoop()
|
||||||
// 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())
|
||||||
|
|| 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.
|
||||||
|
|
|
||||||
|
|
@ -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};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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), true, this);
|
p->prevPath(), p->prevEdge(this), p->prevArc(this),
|
||||||
|
true, this);
|
||||||
search_->saveEnumPath(copy);
|
search_->saveEnumPath(copy);
|
||||||
if (prev_copy)
|
if (prev_copy)
|
||||||
prev_copy->setPrevPath(copy);
|
prev_copy->setPrevPath(copy);
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
std::string_view path_group_name,
|
||||||
const StaState *sta)
|
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 *
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
317
search/Search.cc
317
search/Search.cc
|
|
@ -95,8 +95,8 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SearchAdjLoop::searchFrom(const Vertex *,
|
||||||
const Mode *) const
|
const Mode *) const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SearchAdj::searchThru(Edge *edge,
|
SearchAdjLoop::searchThru(Edge *edge) const
|
||||||
const Mode *) 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 true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SearchAdj::searchTo(const Vertex * /* to_vertex */,
|
SearchAdjLoop::searchTo(const Vertex *,
|
||||||
const Mode *) const
|
const Mode *) const
|
||||||
{
|
{
|
||||||
return true;
|
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 {
|
||||||
|
graph_->visitFanoutEdges(vertex, search_adj_,
|
||||||
|
[this] (Edge *edge,
|
||||||
|
Vertex *fanout) {
|
||||||
|
if (edge->isDisabledLoop()) {
|
||||||
|
if (hasPendingLoopPaths(edge))
|
||||||
|
search_->postponeArrivals(fanout);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
search_->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_);
|
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,26 +2174,6 @@ 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;
|
|
||||||
if (search_->postponeLatchOutputs()) {
|
|
||||||
const Path *from_clk_path = from_clk_info->crprClkPath(this);
|
|
||||||
if (from_clk_path) {
|
|
||||||
Vertex *d_clk_vertex = from_clk_path->vertex(this);
|
|
||||||
Level d_clk_level = d_clk_vertex->level();
|
|
||||||
Level q_level = to_vertex->level();
|
|
||||||
if (d_clk_level >= q_level) {
|
|
||||||
// Crpr clk path on latch data input is required to find Q
|
|
||||||
// arrival. If the data clk path level is >= Q level the
|
|
||||||
// crpr clk path prev_path pointers are not complete.
|
|
||||||
debugPrint(debug_, "search", 3, "postponed latch eval {} {} -> {} {}",
|
|
||||||
d_clk_level, d_clk_vertex->to_string(this),
|
|
||||||
edge->to_string(this), q_level);
|
|
||||||
postponed = true;
|
|
||||||
search_->enqueueLatchOutput(to_vertex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!postponed) {
|
|
||||||
arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max,
|
arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, min_max,
|
||||||
dcalc_ap, sdc);
|
dcalc_ap, sdc);
|
||||||
latches_->latchOutArrival(from_path, arc, edge, to_tag, arc_delay,
|
latches_->latchOutArrival(from_path, arc, edge, to_tag, arc_delay,
|
||||||
|
|
@ -2189,7 +2182,6 @@ PathVisitor::visitFromPath(const Pin *from_pin,
|
||||||
to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_);
|
to_tag = search_->thruTag(to_tag, edge, to_rf, tag_cache_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (from_tag->isClock()) {
|
else if (from_tag->isClock()) {
|
||||||
ClockSet *clks = sdc->findLeafPinClocks(from_pin);
|
ClockSet *clks = sdc->findLeafPinClocks(from_pin);
|
||||||
// Disable edges from hierarchical clock source pins that do
|
// Disable edges from hierarchical clock source pins that do
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,17 +3304,13 @@ 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) {
|
|
||||||
if (group_name == path_group_name_) {
|
|
||||||
Slack end_slack = path_end->slack(sta_);
|
Slack end_slack = path_end->slack(sta_);
|
||||||
if (delayLess(end_slack, slack_, sta_))
|
if (delayLess(end_slack, slack_, sta_))
|
||||||
slack_ = end_slack;
|
slack_ = end_slack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Slack
|
Slack
|
||||||
Sta::endpointSlack(const Pin *pin,
|
Sta::endpointSlack(const Pin *pin,
|
||||||
|
|
@ -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,10 +4433,17 @@ 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);
|
||||||
|
if (network->findNet(parent, escaped)) {
|
||||||
|
report_->warn(1557, "net {} already exists.", name);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Net *net = network->makeNet(escaped, parent);
|
||||||
// Sta notification unnecessary.
|
// Sta notification unnecessary.
|
||||||
return net;
|
return net;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Sta::deleteNet(Net *net)
|
Sta::deleteNet(Net *net)
|
||||||
|
|
@ -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,11 +5103,13 @@ 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();
|
||||||
|
if (edge->isWire()) {
|
||||||
Vertex *from_vertex = edge->from(graph_);
|
Vertex *from_vertex = edge->from(graph_);
|
||||||
delaysInvalidFrom(from_vertex);
|
delaysInvalidFrom(from_vertex);
|
||||||
search_->requiredInvalid(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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 ---"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
|
||||||
|
|
@ -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 ---"
|
||||||
|
|
|
||||||
|
|
@ -536,16 +536,25 @@ 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] } {
|
||||||
|
set object_type [object_type $scene_arg]
|
||||||
|
if { $object_type == "Scene" } {
|
||||||
|
lappend scenes $scene_arg
|
||||||
|
} else {
|
||||||
|
sta_error 135 "scene object type '$object_type' is not a scene."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set scene [find_scene $scene_arg]
|
||||||
if { $scene == "NULL" } {
|
if { $scene == "NULL" } {
|
||||||
sta_error 134 "$scene_name is not the name of a scene."
|
sta_error 134 "$scene_arg is not the name of a scene."
|
||||||
} else {
|
} else {
|
||||||
lappend scenes $scene
|
lappend scenes $scene
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return $scenes
|
return $scenes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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" } {
|
||||||
|
|
|
||||||
19
tcl/Sta.tcl
19
tcl/Sta.tcl
|
|
@ -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]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
[get_scenes *]
|
||||||
|
scene1
|
||||||
|
scene2
|
||||||
|
[get_modes *]
|
||||||
|
mode1
|
||||||
|
mode2
|
||||||
|
|
@ -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 *]
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
=== before rebuild: in1 (ref_pin) ===
|
||||||
|
Startpoint: in1 (input port clocked by clk)
|
||||||
|
Endpoint: r1 (rising edge-triggered flip-flop clocked by clk)
|
||||||
|
Path Group: clk
|
||||||
|
Path Type: max
|
||||||
|
|
||||||
|
Delay Time Description
|
||||||
|
---------------------------------------------------------
|
||||||
|
0.00 0.00 clock clk (rise edge)
|
||||||
|
0.00 0.00 clock network delay (ideal)
|
||||||
|
100.00 100.00 ^ input external delay
|
||||||
|
0.00 100.00 ^ in1 (in)
|
||||||
|
0.00 100.00 ^ r1/D (DFFHQx4_ASAP7_75t_R)
|
||||||
|
100.00 data arrival time
|
||||||
|
|
||||||
|
500.00 500.00 clock clk (rise edge)
|
||||||
|
0.00 500.00 clock network delay (ideal)
|
||||||
|
0.00 500.00 clock reconvergence pessimism
|
||||||
|
500.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R)
|
||||||
|
-11.41 488.59 library setup time
|
||||||
|
488.59 data required time
|
||||||
|
---------------------------------------------------------
|
||||||
|
488.59 data required time
|
||||||
|
-100.00 data arrival time
|
||||||
|
---------------------------------------------------------
|
||||||
|
388.59 slack (MET)
|
||||||
|
|
||||||
|
|
||||||
|
=== before rebuild: in2 (control) ===
|
||||||
|
Startpoint: in2 (input port clocked by clk)
|
||||||
|
Endpoint: r2 (rising edge-triggered flip-flop clocked by clk)
|
||||||
|
Path Group: clk
|
||||||
|
Path Type: max
|
||||||
|
|
||||||
|
Delay Time Description
|
||||||
|
---------------------------------------------------------
|
||||||
|
0.00 0.00 clock clk (rise edge)
|
||||||
|
0.00 0.00 clock network delay (ideal)
|
||||||
|
100.00 100.00 ^ input external delay
|
||||||
|
0.00 100.00 ^ in2 (in)
|
||||||
|
0.00 100.00 ^ r2/D (DFFHQx4_ASAP7_75t_R)
|
||||||
|
100.00 data arrival time
|
||||||
|
|
||||||
|
500.00 500.00 clock clk (rise edge)
|
||||||
|
0.00 500.00 clock network delay (ideal)
|
||||||
|
0.00 500.00 clock reconvergence pessimism
|
||||||
|
500.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R)
|
||||||
|
-13.11 486.89 library setup time
|
||||||
|
486.89 data required time
|
||||||
|
---------------------------------------------------------
|
||||||
|
486.89 data required time
|
||||||
|
-100.00 data arrival time
|
||||||
|
---------------------------------------------------------
|
||||||
|
386.89 slack (MET)
|
||||||
|
|
||||||
|
|
||||||
|
=== after rebuild: in1 (ref_pin) ===
|
||||||
|
Startpoint: in1 (input port clocked by clk)
|
||||||
|
Endpoint: r1 (rising edge-triggered flip-flop clocked by clk)
|
||||||
|
Path Group: clk
|
||||||
|
Path Type: max
|
||||||
|
|
||||||
|
Delay Time Description
|
||||||
|
---------------------------------------------------------
|
||||||
|
0.00 0.00 clock clk (rise edge)
|
||||||
|
0.00 0.00 clock network delay (ideal)
|
||||||
|
100.00 100.00 ^ input external delay
|
||||||
|
0.00 100.00 ^ in1 (in)
|
||||||
|
0.00 100.00 ^ r1/D (DFFHQx4_ASAP7_75t_R)
|
||||||
|
100.00 data arrival time
|
||||||
|
|
||||||
|
500.00 500.00 clock clk (rise edge)
|
||||||
|
0.00 500.00 clock network delay (ideal)
|
||||||
|
0.00 500.00 clock reconvergence pessimism
|
||||||
|
500.00 ^ r1/CLK (DFFHQx4_ASAP7_75t_R)
|
||||||
|
-11.41 488.59 library setup time
|
||||||
|
488.59 data required time
|
||||||
|
---------------------------------------------------------
|
||||||
|
488.59 data required time
|
||||||
|
-100.00 data arrival time
|
||||||
|
---------------------------------------------------------
|
||||||
|
388.59 slack (MET)
|
||||||
|
|
||||||
|
|
||||||
|
=== after rebuild: in2 (control) ===
|
||||||
|
Startpoint: in2 (input port clocked by clk)
|
||||||
|
Endpoint: r2 (rising edge-triggered flip-flop clocked by clk)
|
||||||
|
Path Group: clk
|
||||||
|
Path Type: max
|
||||||
|
|
||||||
|
Delay Time Description
|
||||||
|
---------------------------------------------------------
|
||||||
|
0.00 0.00 clock clk (rise edge)
|
||||||
|
0.00 0.00 clock network delay (ideal)
|
||||||
|
100.00 100.00 ^ input external delay
|
||||||
|
0.00 100.00 ^ in2 (in)
|
||||||
|
0.00 100.00 ^ r2/D (DFFHQx4_ASAP7_75t_R)
|
||||||
|
100.00 data arrival time
|
||||||
|
|
||||||
|
500.00 500.00 clock clk (rise edge)
|
||||||
|
0.00 500.00 clock network delay (ideal)
|
||||||
|
0.00 500.00 clock reconvergence pessimism
|
||||||
|
500.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R)
|
||||||
|
-13.11 486.89 library setup time
|
||||||
|
486.89 data required time
|
||||||
|
---------------------------------------------------------
|
||||||
|
486.89 data required time
|
||||||
|
-100.00 data arrival time
|
||||||
|
---------------------------------------------------------
|
||||||
|
386.89 slack (MET)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# set_input_delay -reference_pin must survive a graph rebuild that keeps SDC
|
||||||
|
# (resizer-like: sta::network_changed_non_sdc deletes the graph but not the
|
||||||
|
# constraints). The ref_pin->input graph edge is owned by the graph; its
|
||||||
|
# existence flag (PortDelay::ref_pin_edges_exist_) must be reset on graph
|
||||||
|
# teardown so ensureInputDelayRefPinEdges() rebuilds the edge. Otherwise in1
|
||||||
|
# loses its arrival seeding and reports "No paths found" after the rebuild.
|
||||||
|
# in2 is a plain input delay (no ref_pin) used as a control.
|
||||||
|
read_liberty asap7_small.lib.gz
|
||||||
|
read_verilog reg1_asap7.v
|
||||||
|
link_design top
|
||||||
|
|
||||||
|
create_clock -name clk -period 500 {clk1 clk2 clk3}
|
||||||
|
set_input_delay -clock clk 100 -reference_pin r2/CLK [get_ports in1]
|
||||||
|
set_input_delay -clock clk 100 [get_ports in2]
|
||||||
|
set_input_transition 10 {in1 in2 clk1 clk2 clk3}
|
||||||
|
set_output_delay -clock clk 1 [get_ports out]
|
||||||
|
|
||||||
|
puts "=== before rebuild: in1 (ref_pin) ==="
|
||||||
|
report_checks -from in1 -path_delay max
|
||||||
|
puts "=== before rebuild: in2 (control) ==="
|
||||||
|
report_checks -from in2 -path_delay max
|
||||||
|
|
||||||
|
sta::network_changed_non_sdc
|
||||||
|
|
||||||
|
puts "=== after rebuild: in1 (ref_pin) ==="
|
||||||
|
report_checks -from in1 -path_delay max
|
||||||
|
puts "=== after rebuild: in2 (control) ==="
|
||||||
|
report_checks -from in2 -path_delay max
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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_);
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue