Merge pull request #318 from The-OpenROAD-Project-staging/sta_update_upstream_lvf_stuff

Sta update upstream lvf stuff
This commit is contained in:
Matt Liberty 2026-03-26 15:50:23 +00:00 committed by GitHub
commit 067cc06e47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
197 changed files with 14503 additions and 14543 deletions

View File

@ -39,7 +39,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14)
cmake_policy(SET CMP0086 NEW)
endif()
project(STA VERSION 3.0.0
project(STA VERSION 3.0.1
LANGUAGES CXX
)
@ -89,13 +89,17 @@ endif()
set(STA_SOURCE
app/StaMain.cc
dcalc/ArcDelayCalc.cc
dcalc/ArcDcalcWaveforms.cc
dcalc/ArcDelayCalc.cc
dcalc/ArnoldiDelayCalc.cc
dcalc/ArnoldiReduce.cc
dcalc/CcsCeffDelayCalc.cc
dcalc/Delay.cc
dcalc/DelayCalc.cc
dcalc/DelayCalcBase.cc
dcalc/DelayNormal.cc
dcalc/DelayScalar.cc
dcalc/DelaySkewNormal.cc
dcalc/DmpCeff.cc
dcalc/DmpDelayCalc.cc
dcalc/FindRoot.cc
@ -106,9 +110,6 @@ set(STA_SOURCE
dcalc/PrimaDelayCalc.cc
dcalc/UnitDelayCalc.cc
graph/DelayFloat.cc
graph/DelayNormal1.cc
graph/DelayNormal2.cc
graph/Graph.cc
graph/GraphCmp.cc
@ -206,6 +207,7 @@ set(STA_SOURCE
search/PathEnum.cc
search/PathExpanded.cc
search/PathGroup.cc
search/PocvMode.cc
search/Property.cc
search/ReportPath.cc
search/Search.cc
@ -408,12 +410,47 @@ find_package(Threads)
find_package(Eigen3 REQUIRED)
include(cmake/FindCUDD.cmake)
if("${SSTA}" STREQUAL "")
set(SSTA 0)
# See if std::format is available and if nor install fmt.
include(CheckCXXSourceCompiles)
set(_sta_fmt_check_saved_flags "${CMAKE_REQUIRED_FLAGS}")
if(MSVC)
string(APPEND CMAKE_REQUIRED_FLAGS " /std:c++20")
else()
string(APPEND CMAKE_REQUIRED_FLAGS " -std=c++20")
endif()
message(STATUS "SSTA: ${SSTA}")
check_cxx_source_compiles("
#include <format>
#include <string>
int main() { (void)std::format(\"{}\", 42); return 0; }
" HAVE_CXX_STD_FORMAT)
set(CMAKE_REQUIRED_FLAGS "${_sta_fmt_check_saved_flags}")
if(HAVE_CXX_STD_FORMAT)
message(STATUS "std::format: available")
else()
# Set the fmt dir for the ubuntu/centos docker files.
if(EXISTS "/usr/local/lib/cmake/fmt/fmt-config.cmake")
set(fmt_DIR "/usr/local/lib/cmake/fmt")
elseif(EXISTS "/usr/lib/x86_64-linux-gnu/cmake/fmt/fmt-config.cmake")
set(fmt_DIR "/usr/lib/x86_64-linux-gnu/cmake/fmt")
elseif(EXISTS "/usr/lib/aarch64-linux-gnu/cmake/fmt/fmt-config.cmake")
set(fmt_DIR "/usr/lib/aarch64-linux-gnu/cmake/fmt")
endif()
find_package(fmt QUIET)
if(fmt_FOUND)
message(STATUS "std::format: using installed fmt library")
else()
message(STATUS "std::format: building fmt library")
include(FetchContent)
FetchContent_Declare(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 11.0.2
)
FetchContent_MakeAvailable(fmt)
endif()
endif()
include(cmake/FindCUDD.cmake)
# configure a header file to pass some of the CMake settings
configure_file(${STA_HOME}/util/StaConfig.hh.cmake
@ -533,6 +570,10 @@ target_link_libraries(OpenSTA
${CUDD_LIB}
)
if(NOT HAVE_CXX_STD_FORMAT)
target_link_libraries(OpenSTA fmt::fmt)
endif()
if (ZLIB_LIBRARIES)
target_link_libraries(OpenSTA ${ZLIB_LIBRARIES})
endif()
@ -561,7 +602,7 @@ endif()
# common to gcc/clang
set(CXX_FLAGS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls
-Wformat-security -Werror=misleading-indentation)
-Wformat-security -Werror=misleading-indentation -Wundef)
if(ENABLE_TSAN)
message(STATUS "Thread sanitizer: ${ENABLE_TSAN}")

View File

@ -68,6 +68,30 @@ cd ../..
rm -rf v1.14.0.tar.gz googletest-1.14.0
EOF
# Download and build fmt
# Ensure the Vault redirect is applied to everything including new installs
RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo && \
sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo && \
sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo && \
yum install -y ca-certificates git && \
update-ca-trust force-enable
# clone fmt compatible version (10.2.1)
RUN git config --global http.sslVerify false && \
git clone --depth 1 --branch 10.2.1 https://github.com/fmtlib/fmt.git /tmp/fmt
RUN source /opt/rh/devtoolset-11/enable && \
cd /tmp/fmt && \
mkdir build && cd build && \
cmake3 .. \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DFMT_TEST=OFF \
-DCMAKE_BUILD_TYPE=Release \
-DFMT_DOC=OFF && \
make -j$(nproc) && \
make install
RUN rm -rf /tmp/fmt
################################################################
FROM base-dependencies AS builder
COPY . /OpenSTA

View File

@ -13,13 +13,14 @@ RUN apt-get update && \
gdb \
tcl-dev \
tcl-tclreadline \
libeigen3-dev \
swig \
bison \
flex \
automake \
autotools-dev \
libgtest-dev
libgtest-dev \
libeigen3-dev \
libfmt-dev
# Download CUDD
RUN wget https://raw.githubusercontent.com/davidkebo/cudd/main/cudd_versions/cudd-3.0.0.tar.gz && \

View File

@ -104,6 +104,7 @@ eigen 3.4.0 3.4.0 MPL2 required
cudd 3.0.0 3.0.0 BSD required
tclreadline 2.3.8 2.3.8 BSD optional
zLib 1.2.5 1.2.8 zlib optional
libfmt 8.1.1 N/A MIT required if std::format not available
```
The [TCL readline library](https://tclreadline.sourceforge.net/tclreadline.html)
@ -143,6 +144,11 @@ make
You can use the "configure --prefix" option and "make install" to install CUDD
in a different directory.
Modern c++ compilers that support c++20 include support for std::format.
With older compilers like gcc 11 on Ubuntu 22.04 and Centos7 the fmt library
is used instead. If it is not installed locally, the github repository is
downloaded and compiled in the build directory.
### Building with CMake
Use the following commands to checkout the git repository and build the

View File

@ -96,11 +96,10 @@ sourceTclFile(const char *filename,
bool verbose,
Tcl_Interp *interp)
{
std::string cmd;
stringPrint(cmd, "sta::include_file %s %s %s",
filename,
echo ? "1" : "0",
verbose ? "1" : "0");
std::string cmd = sta::format("sta::include_file {} {} {}",
filename,
echo ? "1" : "0",
verbose ? "1" : "0");
int code = Tcl_Eval(interp, cmd.c_str());
const char *result = Tcl_GetStringResult(interp);
if (result[0] != '\0')

View File

@ -60,8 +60,9 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg,
bool vdd_exists;
library->supplyVoltage("VDD", vdd, vdd_exists);
if (!vdd_exists)
report->error(1751, "VDD not defined in library %s", library->name());
Waveform in_waveform = driver_waveform->waveform(delayAsFloat(in_slew));
report->error(1751, "VDD not defined in library {}", library->name());
float slew1 = delayAsFloat(in_slew, min_max, sta);
Waveform in_waveform = driver_waveform->waveform(slew1);
// Delay time axis.
FloatSeq time_values;
for (float time : in_waveform.axis1()->values())

View File

@ -94,24 +94,24 @@ makeArcDcalcArg(const char *inst_name,
else {
const Network *network = sta->network();
const Instance *inst = network->instance(in_pin);
report->warn(2100, "no timing arc for %s input/driver pins.",
report->warn(2100, "no timing arc for {} input/driver pins.",
network->pathName(inst));
}
}
else
report->warn(2101, "%s not a valid rise/fall.", drvr_rf_name);
report->warn(2101, "{} not a valid rise/fall.", drvr_rf_name);
}
else
report->warn(2102, "Pin %s/%s not found.", inst_name, drvr_port_name);
report->warn(2102, "Pin {}/{} not found.", inst_name, drvr_port_name);
}
else
report->warn(2103, "%s not a valid rise/fall.", in_rf_name);
report->warn(2103, "{} not a valid rise/fall.", in_rf_name);
}
else
report->warn(2104, "Pin %s/%s not found.", inst_name, in_port_name);
report->warn(2104, "Pin {}/{} not found.", inst_name, in_port_name);
}
else
report->warn(2105, "Instance %s not found.", inst_name);
report->warn(2105, "Instance {} not found.", inst_name);
return ArcDcalcArg();
}
@ -257,18 +257,18 @@ ArcDcalcResult::ArcDcalcResult(size_t load_count) :
}
void
ArcDcalcResult::setGateDelay(ArcDelay gate_delay)
ArcDcalcResult::setGateDelay(const ArcDelay &gate_delay)
{
gate_delay_ = gate_delay;
}
void
ArcDcalcResult::setDrvrSlew(Slew drvr_slew)
ArcDcalcResult::setDrvrSlew(const Slew &drvr_slew)
{
drvr_slew_ = drvr_slew;
}
ArcDelay
const ArcDelay &
ArcDcalcResult::wireDelay(size_t load_idx) const
{
return wire_delays_[load_idx];
@ -276,7 +276,7 @@ ArcDcalcResult::wireDelay(size_t load_idx) const
void
ArcDcalcResult::setWireDelay(size_t load_idx,
ArcDelay wire_delay)
const ArcDelay &wire_delay)
{
wire_delays_[load_idx] = wire_delay;
}
@ -288,7 +288,7 @@ ArcDcalcResult::setLoadCount(size_t load_count)
load_slews_.resize(load_count);
}
Slew
const Slew &
ArcDcalcResult::loadSlew(size_t load_idx) const
{
return load_slews_[load_idx];
@ -296,7 +296,7 @@ ArcDcalcResult::loadSlew(size_t load_idx) const
void
ArcDcalcResult::setLoadSlew(size_t load_idx,
Slew load_slew)
const Slew &load_slew)
{
load_slews_[load_idx] = load_slew;
}

View File

@ -237,7 +237,6 @@ private:
ArnoldiReduce *reduce_;
delay_work *delay_work_;
std::vector<rcmodel*> unsaved_parasitics_;
bool pocv_enabled_;
};
ArcDelayCalc *
@ -397,7 +396,6 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin,
ConcreteParasitic *cparasitic =
reinterpret_cast<ConcreteParasitic*>(const_cast<Parasitic*>(parasitic));
rcmodel_ = dynamic_cast<rcmodel*>(cparasitic);
pocv_enabled_ = variables_->pocvEnabled();
GateTableModel *table_model = arc->gateTableModel(scene, min_max);
if (table_model && rcmodel_) {
const Pvt *pvt = pinPvt(drvr_pin, scene, min_max);
@ -453,8 +451,8 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell,
auto load_idx_itr = load_pin_index_map.find(load_pin);
if (load_idx_itr != load_pin_index_map.end()) {
size_t load_idx = load_idx_itr->second;
ArcDelay wire_delay = _delayV[i] - _delayV[0];
Slew load_slew = _slewV[i];
double wire_delay = _delayV[i] - _delayV[0];
double load_slew = _slewV[i];
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay);
dcalc_result.setLoadSlew(load_idx, load_slew);
@ -1158,7 +1156,7 @@ ra_hinv(double y,
ex = exp(-x);
f = x+ex-1.0-y;
if (f<-1e-8 || f>1e-8)
debugPrint(debug, "arnoldi", 1, "y f %g %g", y, f);
debugPrint(debug, "arnoldi", 1, "y f {:g} {:g}", y, f);
return x;
}
@ -1292,7 +1290,7 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D,
s = s - f/df;
if (std::abs(f)>.5e-12) // .5ps
debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p %g tlohi %s err %s",
debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p {:g} tlohi {} err {}",
p,
units_->timeUnit()->asString(tlohi),
units_->timeUnit()->asString(f));
@ -1325,9 +1323,8 @@ ArnoldiDelayCalc::ra_get_r(delay_work *D,
float c1;
double tlohi,r;
c1 = ctot;
ArcDelay d1;
Slew s1;
tab->table->gateDelay(tab->pvt, tab->in_slew, c1, pocv_enabled_, d1, s1);
float d1, s1;
tab->table->gateDelay(tab->pvt, tab->in_slew, c1, d1, s1);
tlohi = slew_derate*delayAsFloat(s1);
r = tlohi/(c_log*c1);
if (rdelay>0.0 && r > rdelay)
@ -1346,9 +1343,8 @@ ArnoldiDelayCalc::ra_get_s(delay_work *D,
double c_log = con->vlg;
double c_smin = con->smin;
double tlohi,smin,s;
ArcDelay d1;
Slew s1;
tab->table->gateDelay(tab->pvt, tab->in_slew, c, pocv_enabled_, d1, s1);
float d1, s1;
tab->table->gateDelay(tab->pvt, tab->in_slew, c, d1, s1);
tlohi = slew_derate*delayAsFloat(s1);
smin = r*c*c_smin; // c_smin = ra_hinv((1-vhi)/vhi-log(vhi)) + log(vhi);
if (c_log*r*c >= tlohi) {
@ -1378,10 +1374,9 @@ ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab,
float c2 = 0.5*c1;
if (c1==c2)
return 0.0;
ArcDelay d1, d2;
Slew s1, s2;
tab->table->gateDelay(tab->pvt, tab->in_slew, c1, pocv_enabled_, d1, s1);
tab->table->gateDelay(tab->pvt, tab->in_slew, c2, pocv_enabled_, d2, s2);
float d1, d2, s1, s2;
tab->table->gateDelay(tab->pvt, tab->in_slew, c1, d1, s1);
tab->table->gateDelay(tab->pvt, tab->in_slew, c2, d2, s2);
double dt50 = delayAsFloat(d1)-delayAsFloat(d2);
if (dt50 <= 0.0)
return 0.0;
@ -1402,10 +1397,9 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
double vlo = con->vlo;
double ctot = mod->ctot;
double ceff,tlohi,t50_sy,r,s,t50_sr,rdelay;
ArcDelay df;
Slew sf;
float df, sf;
debugPrint(debug_, "arnoldi", 1, "ctot=%s",
debugPrint(debug_, "arnoldi", 1, "ctot={}",
units_->capacitanceUnit()->asString(ctot));
rdelay = ra_rdelay_1(tab,ctot);
@ -1427,24 +1421,23 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
if (debug_->check("arnoldi", 1)) {
double p = 1.0/(r*ctot);
double thix,tlox;
debugPrint(debug_, "arnoldi", 1, "at r=%s s=%s",
debugPrint(debug_, "arnoldi", 1, "at r={} s={}",
units_->resistanceUnit()->asString(r),
units_->timeUnit()->asString(s));
thix = ra_solve_for_t(p,s,vhi);
tlox = ra_solve_for_t(p,s,vlo);
tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, pocv_enabled_, df, sf);
debugPrint(debug_, "arnoldi", 1, "table slew (in_slew %s ctot %s) = %s",
tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, df, sf);
debugPrint(debug_, "arnoldi", 1, "table slew (in_slew {} ctot {}) = {}",
units_->timeUnit()->asString(tab->in_slew),
units_->capacitanceUnit()->asString(ctot),
delayAsString(sf, this));
tlohi = slew_derate*delayAsFloat(sf);
debugPrint(debug_, "arnoldi", 1, "tlohi %s %s",
debugPrint(debug_, "arnoldi", 1, "tlohi {} {}",
units_->timeUnit()->asString(tlohi),
units_->timeUnit()->asString(tlox-thix));
}
ceff = ctot;
tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, pocv_enabled_,
df, sf);
tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, df, sf);
t50_sy = delayAsFloat(df);
t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5);
@ -1475,17 +1468,17 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
// new mvs at ceff
s = ra_get_s(D,tab,r,ceff);
debugPrint(debug_, "arnoldi", 1, "new mvs s = %s",
debugPrint(debug_, "arnoldi", 1, "new mvs s = {}",
units_->timeUnit()->asString(s));
}
}
debugPrint(debug_, "arnoldi", 1, "r %s s %s ceff_time %s ceff %s",
debugPrint(debug_, "arnoldi", 1, "r {} s {} ceff_time {} ceff {}",
units_->resistanceUnit()->asString(r),
units_->timeUnit()->asString(s),
units_->timeUnit()->asString(ceff_time),
units_->capacitanceUnit()->asString(ceff));
tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, pocv_enabled_, df, sf);
tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, df, sf);
t50_sy = delayAsFloat(df);
t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5);
for (j=0;j<mod->n;j++) {

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
// (c) 2018 Nefelus, Inc.
@ -34,6 +34,7 @@
#include "Network.hh"
#include "Units.hh"
#include "Arnoldi.hh"
#include "Format.hh"
#include "parasitics/ConcreteParasiticsPvt.hh"
namespace sta {
@ -43,10 +44,7 @@ rcmodel::rcmodel() :
{
}
rcmodel::~rcmodel()
{
free(pinV);
}
rcmodel::~rcmodel() { free(pinV); }
float
rcmodel::capacitance() const
@ -67,7 +65,7 @@ struct ts_point
ParasiticNode *node_;
int eN;
bool is_term;
int tindex; // index into termV of corresponding term
int tindex; // index into termV of corresponding term
ts_edge **eV;
bool visited;
ts_edge *in_edge;
@ -85,7 +83,6 @@ struct ts_edge
////////////////////////////////////////////////////////////////
const int ArnoldiReduce::ts_point_count_incr_ = 1024;
const int ArnoldiReduce::ts_edge_count_incr_ = 1024;
@ -96,31 +93,32 @@ ArnoldiReduce::ArnoldiReduce(StaState *sta) :
termNmax(256),
dNmax(8)
{
ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point));
ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int));
ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*));
_u0 = (double*)malloc(ts_pointNmax*sizeof(double));
_u1 = (double*)malloc(ts_pointNmax*sizeof(double));
y = (double*)malloc(ts_pointNmax*sizeof(double));
iv = (double*)malloc(ts_pointNmax*sizeof(double));
r = (double*)malloc(ts_pointNmax*sizeof(double));
c = (double*)malloc(ts_pointNmax*sizeof(double));
par = (int*)malloc(ts_pointNmax*sizeof(int));
ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point));
ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int));
ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *));
_u0 = (double *)malloc(ts_pointNmax * sizeof(double));
_u1 = (double *)malloc(ts_pointNmax * sizeof(double));
y = (double *)malloc(ts_pointNmax * sizeof(double));
iv = (double *)malloc(ts_pointNmax * sizeof(double));
r = (double *)malloc(ts_pointNmax * sizeof(double));
c = (double *)malloc(ts_pointNmax * sizeof(double));
par = (int *)malloc(ts_pointNmax * sizeof(int));
ts_edgeV = (ts_edge*)malloc(ts_edgeNmax*sizeof(ts_edge));
ts_stackV = (ts_edge**)malloc(ts_edgeNmax*sizeof(ts_edge*));
ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*));
ts_edgeV = (ts_edge *)malloc(ts_edgeNmax * sizeof(ts_edge));
ts_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *));
ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *));
pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*));
termV = (int*)malloc(termNmax*sizeof(int));
outV = (int*)malloc(termNmax*sizeof(int));
pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *));
termV = (int *)malloc(termNmax * sizeof(int));
outV = (int *)malloc(termNmax * sizeof(int));
d = (double*)malloc(dNmax*sizeof(double));
e = (double*)malloc(dNmax*sizeof(double));
U = (double**)malloc(dNmax*sizeof(double*));
U0 = (double*)malloc(dNmax*termNmax*sizeof(double));
d = (double *)malloc(dNmax * sizeof(double));
e = (double *)malloc(dNmax * sizeof(double));
U = (double **)malloc(dNmax * sizeof(double *));
U0 = (double *)malloc(dNmax * termNmax * sizeof(double));
int h;
for (h=0;h<dNmax;h++) U[h] = U0 + h*termNmax;
for (h = 0; h < dNmax; h++)
U[h] = U0 + h * termNmax;
}
ArnoldiReduce::~ArnoldiReduce()
@ -161,7 +159,7 @@ ArnoldiReduce::reduceToArnoldi(Parasitic *parasitic,
scene_ = scene;
min_max_ = min_max;
parasitics_ = scene->parasitics(min_max);
parasitic_network_ = reinterpret_cast<ConcreteParasiticNetwork*>(parasitic);
parasitic_network_ = reinterpret_cast<ConcreteParasiticNetwork *>(parasitic);
loadWork();
return makeRcmodelDrv();
@ -200,7 +198,7 @@ ArnoldiReduce::loadWork()
ts_edge *e;
int tindex;
for (p = p0; p!=pend; p++) {
for (p = p0; p != pend; p++) {
p->node_ = nullptr;
p->eN = 0;
p->is_term = false;
@ -246,14 +244,14 @@ ArnoldiReduce::loadWork()
e++;
}
for (p=p0;p!=pend;p++) {
for (p = p0; p != pend; p++) {
if (p->node_) {
p->eV = eV;
eV += p->eN;
p->eN = 0;
}
}
for (e=e0;e!=eend;e++) {
for (e = e0; e != eend; e++) {
e->from->eV[e->from->eN++] = e;
if (e->to != e->from)
e->to->eV[e->to->eN++] = e;
@ -267,30 +265,33 @@ ArnoldiReduce::allocPoints()
free(par);
free(c);
free(r);
free(iv); free(y); free(_u1); free(_u0);
free(iv);
free(y);
free(_u1);
free(_u0);
free(ts_pordV);
free(ts_ordV);
free(ts_pointV);
ts_pointNmax = ts_pointN + ts_point_count_incr_;
ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point));
ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int));
ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*));
_u0 = (double*)malloc(ts_pointNmax*sizeof(double));
_u1 = (double*)malloc(ts_pointNmax*sizeof(double));
y = (double*)malloc(ts_pointNmax*sizeof(double));
iv = (double*)malloc(ts_pointNmax*sizeof(double));
r = (double*)malloc(ts_pointNmax*sizeof(double));
c = (double*)malloc(ts_pointNmax*sizeof(double));
par = (int*)malloc(ts_pointNmax*sizeof(int));
ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point));
ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int));
ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *));
_u0 = (double *)malloc(ts_pointNmax * sizeof(double));
_u1 = (double *)malloc(ts_pointNmax * sizeof(double));
y = (double *)malloc(ts_pointNmax * sizeof(double));
iv = (double *)malloc(ts_pointNmax * sizeof(double));
r = (double *)malloc(ts_pointNmax * sizeof(double));
c = (double *)malloc(ts_pointNmax * sizeof(double));
par = (int *)malloc(ts_pointNmax * sizeof(int));
}
if (ts_edgeN > ts_edgeNmax) {
free(ts_edgeV);
free(ts_eV);
free(ts_stackV);
ts_edgeNmax = ts_edgeN + ts_edge_count_incr_;
ts_edgeV = (ts_edge*)malloc(ts_edgeNmax*sizeof(ts_edge));
ts_stackV = (ts_edge**)malloc(ts_edgeNmax*sizeof(ts_edge*));
ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*));
ts_edgeV = (ts_edge *)malloc(ts_edgeNmax * sizeof(ts_edge));
ts_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *));
ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *));
}
}
@ -302,65 +303,69 @@ ArnoldiReduce::allocTerms(int nterms)
free(outV);
free(termV);
free(pinV);
termNmax = nterms+256;
pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*));
termV = (int*)malloc(termNmax*sizeof(int));
outV = (int*)malloc(termNmax*sizeof(int));
termNmax = nterms + 256;
pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *));
termV = (int *)malloc(termNmax * sizeof(int));
outV = (int *)malloc(termNmax * sizeof(int));
U0 = (double*)malloc(dNmax*termNmax*sizeof(double));
U0 = (double *)malloc(dNmax * termNmax * sizeof(double));
int h;
for (h=0;h<dNmax;h++) U[h] = U0 + h*termNmax;
for (h = 0; h < dNmax; h++)
U[h] = U0 + h * termNmax;
}
}
ts_point *
ArnoldiReduce::findPt(ParasiticNode *node)
{
return &ts_pointV[pt_map_[reinterpret_cast<ConcreteParasiticNode*>(node)]];
return &ts_pointV[pt_map_[reinterpret_cast<ConcreteParasiticNode *>(node)]];
}
rcmodel *
ArnoldiReduce::makeRcmodelDrv()
{
ParasiticNode *drv_node =
parasitics_->findParasiticNode(parasitic_network_, drvr_pin_);
parasitics_->findParasiticNode(parasitic_network_, drvr_pin_);
ts_point *pdrv = findPt(drv_node);
makeRcmodelDfs(pdrv);
getRC();
if (ctot_ < 1e-22) // 1e-10ps
if (ctot_ < 1e-22) // 1e-10ps
return nullptr;
setTerms(pdrv);
makeRcmodelFromTs();
return makeRcmodelFromW();
}
#define ts_orient( pp, ee) \
if (ee->from!=pp) { ee->to = ee->from; ee->from = pp; }
#define ts_orient(pp, ee) \
if (ee->from != pp) { \
ee->to = ee->from; \
ee->from = pp; \
}
void
ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv)
{
bool loop = false;
int k;
ts_point *p,*q;
ts_point *p, *q;
ts_point *p0 = ts_pointV;
ts_point *pend = p0 + ts_pointN;
for (p=p0;p!=pend;p++)
for (p = p0; p != pend; p++)
p->visited = 0;
ts_edge *e;
ts_edge **stackV = ts_stackV;
int stackN = 1;
stackV[0] = e = pdrv->eV[0];
ts_orient(pdrv,e);
ts_orient(pdrv, e);
pdrv->visited = 1;
pdrv->in_edge = nullptr;
pdrv->ts = 0;
ts_ordV[0] = pdrv-p0;
ts_ordV[0] = pdrv - p0;
ts_pordV[0] = pdrv;
ts_ordN = 1;
while (stackN>0) {
e = stackV[stackN-1];
while (stackN > 0) {
e = stackV[stackN - 1];
q = e->to;
if (q->visited) {
@ -368,47 +373,53 @@ ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv)
// ignore, and do not even set *loop
if (e->to != e->from)
loop = true;
} else {
}
else {
// try to descend
q->visited = 1;
q->ts = ts_ordN++;
ts_pordV[q->ts] = q;
ts_ordV[q->ts] = q-p0;
ts_ordV[q->ts] = q - p0;
q->in_edge = e;
if (q->eN>1) {
for (k=0;k<q->eN;k++) if (q->eV[k] != e) break;
if (q->eN > 1) {
for (k = 0; k < q->eN; k++)
if (q->eV[k] != e)
break;
e = q->eV[k];
ts_orient(q,e);
ts_orient(q, e);
stackV[stackN++] = e;
continue; // descent
continue; // descent
}
}
// try to ascend
while (--stackN>=0) {
while (--stackN >= 0) {
e = stackV[stackN];
p = e->from;
// find e in p->eV
for (k=0;k<p->eN;k++) if (p->eV[k]==e) break;
for (k = 0; k < p->eN; k++)
if (p->eV[k] == e)
break;
// if (k==p->eN) notice(0,"ERROR, e not found!\n");
++k;
if (k>=p->eN) continue;
if (k >= p->eN)
continue;
e = p->eV[k];
// check that next sibling is not the incoming edge
if (stackN>0 && e==stackV[stackN-1]) {
++k;
if (k>=p->eN) continue;
e = p->eV[k];
if (stackN > 0 && e == stackV[stackN - 1]) {
++k;
if (k >= p->eN)
continue;
e = p->eV[k];
}
ts_orient(p,e);
ts_orient(p, e);
stackV[stackN++] = e;
break;
}
} // while (stackN)
} // while (stackN)
if (loop)
debugPrint(debug_, "arnoldi", 1, "net %s loop",
network_->pathName(drvr_pin_));
debugPrint(debug_, "arnoldi", 1, "net {} loop", network_->pathName(drvr_pin_));
}
// makeRcmodelGetRC
@ -418,13 +429,12 @@ ArnoldiReduce::getRC()
ts_point *p, *p0 = ts_pointV;
ts_point *pend = p0 + ts_pointN;
ctot_ = 0.0;
for (p=p0;p!=pend;p++) {
for (p = p0; p != pend; p++) {
p->c = 0.0;
p->r = 0.0;
if (p->node_) {
ParasiticNode *node = p->node_;
double cap = parasitics_->nodeGndCap(node)
+ pinCapacitance(node);
double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
if (cap > 0.0) {
p->c = cap;
ctot_ += cap;
@ -433,11 +443,9 @@ ArnoldiReduce::getRC()
p->c = 0.0;
if (p->in_edge && p->in_edge->resistor_)
p->r = parasitics_->value(p->in_edge->resistor_);
if (!(p->r>=0.0 && p->r<100e+3)) { // 0 < r < 100kohm
debugPrint(debug_, "arnoldi", 1,
"R value %g out of range, drvr pin %s",
p->r,
network_->pathName(drvr_pin_));
if (!(p->r >= 0.0 && p->r < 100e+3)) { // 0 < r < 100kohm
debugPrint(debug_, "arnoldi", 1, "R value {:g} out of range, drvr pin {}",
p->r, network_->pathName(drvr_pin_));
}
}
}
@ -466,7 +474,7 @@ ArnoldiReduce::pinCapacitance(ParasiticNode *node)
LibertyPort *lib_port = network_->libertyPort(port);
const Sdc *sdc = scene_->sdc();
if (lib_port)
pin_cap = sdc->pinCapacitance(pin,rf_, scene_, min_max_);
pin_cap = sdc->pinCapacitance(pin, rf_, scene_, min_max_);
else if (network_->isTopLevelPort(pin))
pin_cap = sdc->portExtCap(port, rf_, min_max_);
}
@ -479,13 +487,15 @@ ArnoldiReduce::setTerms(ts_point *pdrv)
// termV: from drv-ordered to fixed order
// outV: from drv-ordered to ts_pordV
ts_point *p;
int k,k0;
int k, k0;
termV[0] = k0 = pdrv->tindex;
for (k=1;k<termN;k++) {
if (k==k0) termV[k] = 0;
else termV[k] = k;
for (k = 1; k < termN; k++) {
if (k == k0)
termV[k] = 0;
else
termV[k] = k;
}
for (k=0;k<termN;k++) {
for (k = 0; k < termN; k++) {
p = pterm0 + termV[k];
outV[k] = p->ts;
}
@ -498,38 +508,37 @@ ArnoldiReduce::makeRcmodelFromTs()
ts_point *p, *p0 = ts_pointV;
int n = ts_ordN;
int nterms = termN;
int i,j,k,h;
int i, j, k, h;
if (debug_->check("arnoldi", 1)) {
for (k=0;k<ts_ordN;k++) {
for (k = 0; k < ts_ordN; k++) {
p = ts_pordV[k];
debugPrint(debug_, "arnoldi", 1, "T%d,P%ld c=%s",
p->ts,
p-p0,
debugPrint(debug_, "arnoldi", 1, "T{} P{} c={}", p->ts, p - p0,
units_->capacitanceUnit()->asString(p->c));
if (p->is_term)
debugPrint(debug_, "arnoldi", 1, " term %d", p->tindex);
debugPrint(debug_, "arnoldi", 1, " term {}", p->tindex);
if (p->in_edge)
debugPrint(debug_, "arnoldi", 1, " from T%d,P%ld r=%s",
p->in_edge->from->ts,
p->in_edge->from-p0,
debugPrint(debug_, "arnoldi", 1, " from T{} P{} r={}",
p->in_edge->from->ts, p->in_edge->from - p0,
units_->resistanceUnit()->asString(p->r));
}
for (i=0;i<nterms;i++)
debugPrint(debug_, "arnoldi", 1, "outV[%d] = T%d", i, outV[i]);
for (i = 0; i < nterms; i++)
debugPrint(debug_, "arnoldi", 1, "outV[{}] = T{}", i, outV[i]);
}
int max_order = 5;
double *u0, *u1;
u0 = _u0; u1 = _u1;
double sum,e1;
u0 = _u0;
u1 = _u1;
double sum, e1;
order = max_order;
if (n < order)
order = n;
par[0] = -1; r[0] = 0.0;
par[0] = -1;
r[0] = 0.0;
c[0] = ts_pordV[0]->c;
for (j=1;j<n;j++) {
for (j = 1; j < n; j++) {
p = ts_pordV[j];
c[j] = p->c;
r[j] = p->r;
@ -537,92 +546,99 @@ ArnoldiReduce::makeRcmodelFromTs()
}
sum = 0.0;
for (j=0;j<n;j++) sum += c[j];
debugPrint(debug_, "arnoldi", 1, "ctot = %s",
for (j = 0; j < n; j++)
sum += c[j];
debugPrint(debug_, "arnoldi", 1, "ctot = {}",
units_->capacitanceUnit()->asString(sum));
ctot_ = sum;
sqc_ = sqrt(sum);
double sqrt_ctot_inv = 1.0/sqc_;
for (j=0;j<n;j++) u0[j] = sqrt_ctot_inv;
for (h=0;h<order;h++) {
for (i=0;i<nterms;i++) U[h][i] = u0[outV[i]];
double sqrt_ctot_inv = 1.0 / sqc_;
for (j = 0; j < n; j++)
u0[j] = sqrt_ctot_inv;
for (h = 0; h < order; h++) {
for (i = 0; i < nterms; i++)
U[h][i] = u0[outV[i]];
// y = R C u0
for (j=0;j<n;j++) {
for (j = 0; j < n; j++) {
iv[j] = 0.0;
}
for (j=n-1;j>0;j--) {
iv[j] += c[j]*u0[j];
for (j = n - 1; j > 0; j--) {
iv[j] += c[j] * u0[j];
iv[par[j]] += iv[j];
}
iv[0] += c[0]*u0[0];
iv[0] += c[0] * u0[0];
y[0] = 0.0;
for (j=1;j<n;j++) {
y[j] = y[par[j]] + r[j]*iv[j];
for (j = 1; j < n; j++) {
y[j] = y[par[j]] + r[j] * iv[j];
}
// d[h] = u0 C y
sum = 0.0;
for (j=1;j<n;j++) {
sum += u0[j]*c[j]*y[j];
for (j = 1; j < n; j++) {
sum += u0[j] * c[j] * y[j];
}
d[h] = sum;
if (h==order-1) break;
if (d[h]<1e-13) { // .1ps
order = h+1;
break;
if (h == order - 1)
break;
if (d[h] < 1e-13) { // .1ps
order = h + 1;
break;
}
// y = y - d[h]*u0 - e[h-1]*u1
if (h==0) {
for (j=0;j<n;j++) y[j] -= sum*u0[j];
} else {
e1 = e[h-1];
for (j=0;j<n;j++) y[j] -= sum*u0[j] + e1*u1[j];
if (h == 0) {
for (j = 0; j < n; j++)
y[j] -= sum * u0[j];
}
else {
e1 = e[h - 1];
for (j = 0; j < n; j++)
y[j] -= sum * u0[j] + e1 * u1[j];
}
// e[h] = sqrt(y C y)
// u1 = y/e[h]
sum = 0.0;
for (j=0;j<n;j++) {
sum += c[j]*y[j]*y[j];
for (j = 0; j < n; j++) {
sum += c[j] * y[j] * y[j];
}
if (sum<1e-30) { // (1e-6ns)^2
order = h+1;
if (sum < 1e-30) { // (1e-6ns)^2
order = h + 1;
break;
}
e[h] = sqrt(sum);
sum = 1.0/e[h];
for (j=0;j<n;j++) u1[j] = sum*y[j];
sum = 1.0 / e[h];
for (j = 0; j < n; j++)
u1[j] = sum * y[j];
// swap u0, u1
if (h%2) {
u0 = _u0; u1 = _u1;
} else {
u0 = _u1; u1 = _u0;
if (h % 2) {
u0 = _u0;
u1 = _u1;
}
else {
u0 = _u1;
u1 = _u0;
}
}
if (debug_->check("arnoldi", 1)) {
report_->reportLine("tridiagonal reduced matrix, drvr pin %s",
network_->pathName(drvr_pin_));
report_->reportLine("order %d n %d",order,n);
for (h=0;h<order;h++) {
if (h<order-1)
report_->reportLine(" d[%d] %s e[%d] %s",
h,
units_->timeUnit()->asString(d[h]),
h,
units_->timeUnit()->asString(e[h]));
report_->report("tridiagonal reduced matrix, drvr pin {}",
network_->pathName(drvr_pin_));
report_->report("order {} n {}", order, n);
for (h = 0; h < order; h++) {
if (h < order - 1)
report_->report(" d[{}] {} e[{}] {}", h,
units_->timeUnit()->asString(d[h]), h,
units_->timeUnit()->asString(e[h]));
else
report_->reportLine(" d[%d] %s",
h,
units_->timeUnit()->asString(d[h]));
std::string line = stdstrPrint("U[%d]",h);
for (i=0;i<nterms;i++)
line += stdstrPrint(" %6.2e",U[h][i]);
report_->reportLineString(line);
report_->report(" d[{}] {}", h, units_->timeUnit()->asString(d[h]));
std::string line = sta::format("U[{}]", h);
for (i = 0; i < nterms; i++)
line += sta::format(" {:6.2e}", U[h][i]);
report_->reportLine(line);
}
}
}
@ -630,29 +646,33 @@ ArnoldiReduce::makeRcmodelFromTs()
rcmodel *
ArnoldiReduce::makeRcmodelFromW()
{
int j,h;
int j, h;
int n = termN;
rcmodel *mod = new rcmodel();
mod->order = order;
mod->n = n;
if (order>0) {
int totd = order + order - 1 + order*n;
mod->d = (double *)malloc(totd*sizeof(double));
if (order>1) mod->e = mod->d + order;
else mod->e = nullptr;
mod->U = (double **)malloc(order*sizeof(double*));
if (order > 0) {
int totd = order + order - 1 + order * n;
mod->d = (double *)malloc(totd * sizeof(double));
if (order > 1)
mod->e = mod->d + order;
else
mod->e = nullptr;
mod->U = (double **)malloc(order * sizeof(double *));
mod->U[0] = mod->d + order + order - 1;
for (h=1;h<order;h++) mod->U[h]=mod->U[0] + h*n;
for (h=0;h<order;h++) {
for (h = 1; h < order; h++)
mod->U[h] = mod->U[0] + h * n;
for (h = 0; h < order; h++) {
mod->d[h] = d[h];
if (h<order-1) mod->e[h] = e[h];
for (j=0;j<n;j++)
if (h < order - 1)
mod->e[h] = e[h];
for (j = 0; j < n; j++)
mod->U[h][j] = U[h][j];
}
}
mod->pinV = (const Pin **)malloc(n*sizeof(const Pin*));
for (j=0;j<n;j++) {
mod->pinV = (const Pin **)malloc(n * sizeof(const Pin *));
for (j = 0; j < n; j++) {
int k = termV[j];
mod->pinV[j] = pinV[k];
}
@ -662,4 +682,4 @@ ArnoldiReduce::makeRcmodelFromW()
return mod;
}
} // namespace
} // namespace sta

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "CcsCeffDelayCalc.hh"
@ -63,10 +63,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
{
}
CcsCeffDelayCalc::~CcsCeffDelayCalc()
{
delete table_dcalc_;
}
CcsCeffDelayCalc::~CcsCeffDelayCalc() { delete table_dcalc_; }
ArcDelayCalc *
CcsCeffDelayCalc::copy()
@ -90,13 +87,13 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
parasitic_ = parasitic;
output_waveforms_ = nullptr;
GateTableModel *table_model = arc->gateTableModel(scene, min_max);
const GateTableModel *table_model = arc->gateTableModel(scene, min_max);
if (table_model && parasitic) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
Parasitics *parasitics = scene->parasitics(min_max);
parasitics->piModel(parasitic, c2_, rpi_, c1_);
if (output_waveforms
&& rpi_ > 0.0 && c1_ > 0.0
if (output_waveforms && rpi_ > 0.0
&& c1_ > 0.0
// Bounds check because extrapolating waveforms does not work for shit.
&& output_waveforms->slewAxis()->inBounds(in_slew_)
&& output_waveforms->capAxis()->inBounds(c2_)
@ -107,22 +104,44 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
drvr_rf_ = arc->toEdge()->asRiseFall();
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!vdd_exists)
report_->error(1700, "VDD not defined in library %s", drvr_library->name());
report_->error(1700, "VDD not defined in library {}", drvr_library->name());
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
drvr_cell->ensureVoltageWaveforms(scenes_);
in_slew_ = delayAsFloat(in_slew);
output_waveforms_ = output_waveforms;
ref_time_ = output_waveforms_->referenceTime(in_slew_);
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
debugPrint(debug_, "ccs_dcalc", 1, "{} {}",
drvr_cell->name(),
drvr_rf_->shortName());
ArcDelay gate_delay;
Slew drvr_slew;
gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew);
return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map);
double gate_delay, drvr_slew;
gateDelaySlew(drvr_library, gate_delay, drvr_slew);
debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}",
delayAsString(gate_delay, this), delayAsString(drvr_slew, this));
// Fill in pocv parameters.
ArcDelay gate_delay2(gate_delay);
Slew drvr_slew2(drvr_slew);
if (variables_->pocvEnabled()) {
double ceff = region_ceff_[0];
const Pvt *pvt = pinPvt(drvr_pin_, scene, min_max);
table_model->gateDelayPocv(pvt, in_slew_, ceff, min_max,
variables_->pocvMode(),
gate_delay2, drvr_slew2);
}
ArcDcalcResult dcalc_result(load_pin_index_map.size());
dcalc_result.setGateDelay(gate_delay2);
dcalc_result.setDrvrSlew(drvr_slew2);
for (const auto &[load_pin, load_idx] : load_pin_index_map) {
double wire_delay, load_slew;
loadDelaySlew(load_pin, drvr_library, drvr_slew, wire_delay, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay);
dcalc_result.setLoadSlew(load_idx, load_slew);
}
return dcalc_result;
}
}
return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
@ -131,30 +150,29 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
void
CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
const RiseFall *rf,
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew)
double &gate_delay,
double &drvr_slew)
{
initRegions(drvr_library, rf);
initRegions(drvr_library, drvr_rf_);
findCsmWaveform();
ref_time_ = output_waveforms_->referenceTime(in_slew_);
gate_delay = region_times_[region_vth_idx_] - ref_time_;
drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
debugPrint(debug_, "ccs_dcalc", 2,
"gate_delay %s drvr_slew %s (initial)",
"gate_delay {} drvr_slew {} (initial)",
delayAsString(gate_delay, this),
delayAsString(drvr_slew, this));
float prev_drvr_slew = delayAsFloat(drvr_slew);
float prev_drvr_slew = drvr_slew;
constexpr int max_iterations = 5;
for (int iter = 0; iter < max_iterations; iter++) {
debugPrint(debug_, "ccs_dcalc", 2, "iteration %d", iter);
debugPrint(debug_, "ccs_dcalc", 2, "iteration {}", iter);
// Init drvr ramp model for vl.
for (size_t i = 0; i <= region_count_; i++) {
region_ramp_times_[i] = region_times_[i];
if (i < region_count_)
region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i])
/ (region_times_[i + 1] - region_times_[i]);
/ (region_times_[i + 1] - region_times_[i]);
}
for (size_t i = 0; i < region_count_; i++) {
@ -174,20 +192,19 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
double q2 = v2 * c2_ + c1_v2 * c1_;
double ceff = (q2 - q1) / (v2 - v1);
debugPrint(debug_, "ccs_dcalc", 2, "ceff %s",
debugPrint(debug_, "ccs_dcalc", 2, "ceff {}",
capacitance_unit_->asString(ceff));
region_ceff_[i] = ceff;
}
findCsmWaveform();
gate_delay = region_times_[region_vth_idx_] - ref_time_;
drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
debugPrint(debug_, "ccs_dcalc", 2,
"gate_delay %s drvr_slew %s",
debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}",
delayAsString(gate_delay, this),
delayAsString(drvr_slew, this));
if (std::abs(delayAsFloat(drvr_slew) - prev_drvr_slew) < .01 * prev_drvr_slew)
if (std::abs(drvr_slew - prev_drvr_slew) < .01 * prev_drvr_slew)
break;
prev_drvr_slew = delayAsFloat(drvr_slew);
prev_drvr_slew = drvr_slew;
}
}
@ -217,68 +234,68 @@ CcsCeffDelayCalc::initRegions(const LibertyLibrary *drvr_library,
double vth_vh = (vh_ - vth_);
switch (region_count_) {
case 4:
region_vth_idx_ = 2;
region_volts_ = {0.0, vl_, vth_, vh_, vdd_};
break;
case 5: {
region_vth_idx_ = 2;
double v1 = vth_ + .7 * vth_vh;
region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_};
break;
}
case 6: {
region_vth_idx_ = 2;
double v1 = vth_ + .3 * vth_vh;
double v2 = vth_ + .6 * vth_vh;
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_};
break;
}
case 7: {
region_vth_idx_ = 2;
region_vh_idx_ = 5;
double v1 = vth_ + .3 * vth_vh;
double v2 = vth_ + .6 * vth_vh;
double v3 = vh_ + .5 * (vdd_ - vh_);
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_};
break;
}
case 8: {
region_vth_idx_ = 2;
region_vh_idx_ = 6;
double v1 = vth_ + .25 * vth_vh;
double v2 = vth_ + .50 * vth_vh;
double v3 = vth_ + .75 * vth_vh;
double v4 = vh_ + .5 * (vdd_ - vh_);
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_};
break;
}
case 9: {
region_vth_idx_ = 2;
region_vh_idx_ = 7;
double v1 = vth_ + .2 * vth_vh;
double v2 = vth_ + .4 * vth_vh;
double v3 = vth_ + .6 * vth_vh;
double v4 = vth_ + .8 * vth_vh;
double v5 = vh_ + .5 * (vdd_ - vh_);
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_};
break;
}
case 10: {
region_vth_idx_ = 2;
region_vh_idx_ = 7;
double v1 = vth_ + .2 * vth_vh;
double v2 = vth_ + .4 * vth_vh;
double v3 = vth_ + .6 * vth_vh;
double v4 = vth_ + .8 * vth_vh;
double v5 = vh_ + .3 * (vdd_ - vh_);
double v6 = vh_ + .6 * (vdd_ - vh_);
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_};
break;
}
default:
report_->error(1701, "unsupported ccs region count.");
break;
case 4:
region_vth_idx_ = 2;
region_volts_ = {0.0, vl_, vth_, vh_, vdd_};
break;
case 5: {
region_vth_idx_ = 2;
double v1 = vth_ + .7 * vth_vh;
region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_};
break;
}
case 6: {
region_vth_idx_ = 2;
double v1 = vth_ + .3 * vth_vh;
double v2 = vth_ + .6 * vth_vh;
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_};
break;
}
case 7: {
region_vth_idx_ = 2;
region_vh_idx_ = 5;
double v1 = vth_ + .3 * vth_vh;
double v2 = vth_ + .6 * vth_vh;
double v3 = vh_ + .5 * (vdd_ - vh_);
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_};
break;
}
case 8: {
region_vth_idx_ = 2;
region_vh_idx_ = 6;
double v1 = vth_ + .25 * vth_vh;
double v2 = vth_ + .50 * vth_vh;
double v3 = vth_ + .75 * vth_vh;
double v4 = vh_ + .5 * (vdd_ - vh_);
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_};
break;
}
case 9: {
region_vth_idx_ = 2;
region_vh_idx_ = 7;
double v1 = vth_ + .2 * vth_vh;
double v2 = vth_ + .4 * vth_vh;
double v3 = vth_ + .6 * vth_vh;
double v4 = vth_ + .8 * vth_vh;
double v5 = vh_ + .5 * (vdd_ - vh_);
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_};
break;
}
case 10: {
region_vth_idx_ = 2;
region_vh_idx_ = 7;
double v1 = vth_ + .2 * vth_vh;
double v2 = vth_ + .4 * vth_vh;
double v3 = vth_ + .6 * vth_vh;
double v4 = vth_ + .8 * vth_vh;
double v5 = vh_ + .3 * (vdd_ - vh_);
double v6 = vh_ + .6 * (vdd_ - vh_);
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_};
break;
}
default:
report_->error(1701, "unsupported ccs region count.");
break;
}
fill(region_ceff_.begin(), region_ceff_.end(), c2_ + c1_);
}
@ -287,15 +304,15 @@ void
CcsCeffDelayCalc::findCsmWaveform()
{
for (size_t i = 0; i < region_count_; i++) {
double t1 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
region_volts_[i]);
double t1 =
output_waveforms_->voltageTime(in_slew_, region_ceff_[i], region_volts_[i]);
double t2 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
region_volts_[i + 1]);
region_begin_times_[i] = t1;
region_end_times_[i] = t2;
double time_offset = (i == 0)
? 0.0
: t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]);
? 0.0
: t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]);
region_time_offsets_[i] = time_offset;
if (i == 0)
@ -306,74 +323,48 @@ CcsCeffDelayCalc::findCsmWaveform()
////////////////////////////////////////////////////////////////
ArcDcalcResult
CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library,
const RiseFall *rf,
ArcDelay &gate_delay,
Slew &drvr_slew,
const LoadPinIndexMap &load_pin_index_map)
{
ArcDcalcResult dcalc_result(load_pin_index_map.size());
debugPrint(debug_, "ccs_dcalc", 2,
"gate_delay %s drvr_slew %s",
delayAsString(gate_delay, this),
delayAsString(drvr_slew, this));
dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew);
for (const auto &[load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay;
Slew load_slew;
loadDelaySlew(load_pin, drvr_library, rf, drvr_slew, wire_delay, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay);
dcalc_result.setLoadSlew(load_idx, load_slew);
}
return dcalc_result;
}
void
CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
const LibertyLibrary *drvr_library,
const RiseFall *rf,
Slew &drvr_slew,
double drvr_slew,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew)
double &wire_delay,
double &load_slew)
{
wire_delay = 0.0;
load_slew = drvr_slew;
bool elmore_exists = false;
float elmore = 0.0;
if (parasitic_
&& parasitics_->isPiElmore(parasitic_))
if (parasitic_ && parasitics_->isPiElmore(parasitic_))
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
if (elmore_exists &&
(elmore == 0.0
// Elmore delay is small compared to driver slew.
|| elmore < delayAsFloat(drvr_slew) * 1e-3)) {
|| elmore < drvr_slew * 1e-3)) {
wire_delay = elmore;
load_slew = drvr_slew;
}
else
loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay, load_slew);
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
thresholdAdjust(load_pin, drvr_library, drvr_rf_, wire_delay, load_slew);
}
void
CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
Slew &drvr_slew,
double &drvr_slew,
float elmore,
// Return values.
ArcDelay &delay,
Slew &slew)
double &delay,
double &slew)
{
for (size_t i = 0; i <= region_count_; i++) {
region_ramp_times_[i] = region_times_[i];
if (i < region_count_)
region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i])
/ (region_times_[i + 1] - region_times_[i]);
/ (region_times_[i + 1] - region_times_[i]);
}
vl_fail_ = false;
@ -389,10 +380,8 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
slew = drvr_slew;
fail("load delay threshold crossing");
}
debugPrint(debug_, "ccs_dcalc", 2,
"load %s delay %s slew %s",
network_->pathName(load_pin),
delayAsString(delay, this),
debugPrint(debug_, "ccs_dcalc", 2, "load {} delay {} slew {}",
network_->pathName(load_pin), delayAsString(delay, this),
delayAsString(slew, this));
}
@ -457,12 +446,12 @@ CcsCeffDelayCalc::findVlTime(double v,
double t_init = region_ramp_times_[0];
double t_final = region_ramp_times_[region_count_];
bool root_fail = false;
double time = findRoot([&] (double t,
double &y,
double &dy) {
vl(t, elmore, y, dy);
y -= v;
}, t_init, t_final + elmore * 3.0, .001, 20, root_fail);
double time = findRoot(
[&](double t, double &y, double &dy) {
vl(t, elmore, y, dy);
y -= v;
},
t_init, t_final + elmore * 3.0, .001, 20, root_fail);
vl_fail_ |= root_fail;
return time;
}
@ -487,7 +476,7 @@ PinSeq
CcsCeffDelayCalc::watchPins() const
{
PinSeq pins;
for (const auto& [pin, values] : watch_pin_values_)
for (const auto &[pin, values] : watch_pin_values_)
pins.push_back(pin);
return pins;
}
@ -523,8 +512,8 @@ CcsCeffDelayCalc::drvrWaveform()
drvr_volts->push_back(v);
}
}
TableAxisPtr drvr_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
std::move(*drvr_times));
TableAxisPtr drvr_time_axis =
std::make_shared<TableAxis>(TableAxisVariable::time, std::move(*drvr_times));
delete drvr_times;
Table drvr_table(drvr_volts, drvr_time_axis);
return drvr_table;
@ -555,8 +544,8 @@ CcsCeffDelayCalc::loadWaveform(const Pin *load_pin)
double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v;
load_volts->push_back(v1);
}
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
std::move(*load_times));
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(
TableAxisVariable::time, std::move(*load_times));
delete load_times;
Table load_table(load_volts, load_time_axis);
return load_table;
@ -578,10 +567,9 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin,
float elmore = 0.0;
if (parasitic_) {
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
drvr_rf, scene, min_max);
if (dcalc_success
&& elmore_exists) {
bool dcalc_success =
makeWaveformPreamble(in_pin, in_rf, drvr_pin, drvr_rf, scene, min_max);
if (dcalc_success && elmore_exists) {
FloatSeq *load_times = new FloatSeq;
FloatSeq *load_volts = new FloatSeq;
for (size_t j = 0; j <= region_count_; j++) {
@ -600,8 +588,8 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin,
double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
load_volts->push_back(v1);
}
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
std::move(*load_times));
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(
TableAxisVariable::time, std::move(*load_times));
delete load_times;
Table load_table(load_volts, load_time_axis);
return load_table;
@ -630,7 +618,7 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin,
break;
}
if (edge) {
TimingArc *arc = nullptr;
TimingArc *arc = nullptr;
for (TimingArc *arc1 : edge->timingArcSet()->arcs()) {
if (arc1->fromEdge()->asRiseFall() == in_rf
&& arc1->toEdge()->asRiseFall() == drvr_rf) {
@ -645,9 +633,9 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin,
if (parasitic_) {
parasitics_->piModel(parasitic_, c2_, rpi_, c1_);
LoadPinIndexMap load_pin_index_map =
graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_,
load_pin_index_map, scene, min_max);
graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_, load_pin_index_map,
scene, min_max);
return true;
}
}
@ -671,12 +659,12 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
Parasitic *pi_elmore = nullptr;
const RiseFall *rf = arc->toEdge()->asRiseFall();
if (parasitic && !parasitics_->isPiElmore(parasitic)) {
pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf,
scene, min_max);
pi_elmore =
parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, scene, min_max);
}
std::string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap,
pi_elmore, load_pin_index_map,
scene, min_max, digits);
std::string report =
table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, pi_elmore,
load_pin_index_map, scene, min_max, digits);
parasitics_->deleteDrvrReducedParasitics(drvr_pin);
return report;
}
@ -686,7 +674,7 @@ CcsCeffDelayCalc::fail(const char *reason)
{
// Report failures with a unique debug flag.
if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1))
report_->reportLine("delay_calc: CCS failed - %s", reason);
report_->report("delay_calc: CCS failed - {}", reason);
}
} // namespace
} // namespace sta

View File

@ -71,31 +71,24 @@ protected:
typedef std::vector<double> Region;
void gateDelaySlew(const LibertyLibrary *drvr_library,
const RiseFall *rf,
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew);
double &gate_delay,
double &drvr_slew);
void initRegions(const LibertyLibrary *drvr_library,
const RiseFall *rf);
void findCsmWaveform();
ArcDcalcResult makeResult(const LibertyLibrary *drvr_library,
const RiseFall *rf,
ArcDelay &gate_delay,
Slew &drvr_slew,
const LoadPinIndexMap &load_pin_index_map);
void loadDelaySlew(const Pin *load_pin,
const LibertyLibrary *drvr_library,
const RiseFall *rf,
Slew &drvr_slew,
double drvr_slew,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew);
double &wire_delay,
double &load_slew);
void loadDelaySlew(const Pin *load_pin,
Slew &drvr_slew,
double &drvr_slew,
float elmore,
// Return values.
ArcDelay &delay,
Slew &slew);
double &delay,
double &slew);
double findVlTime(double v,
double elmore);
bool makeWaveformPreamble(const Pin *in_pin,

523
dcalc/Delay.cc Normal file
View File

@ -0,0 +1,523 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#include "Delay.hh"
#include <cmath>
#include "StaConfig.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "StaState.hh"
#include "Variables.hh"
namespace sta {
static Delay delay_init_values[MinMax::index_count];
void
initDelayConstants()
{
delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue();
delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue();
}
Delay::Delay() :
values_{0.0, 0.0, 0.0, 0.0}
{
}
Delay::Delay(float mean) :
values_{mean, 0.0, 0.0, 0.0}
{
}
Delay::Delay(float mean,
float std_dev2) :
values_{mean, 0.0, std_dev2, 0.0}
{
}
Delay::Delay(float mean,
float mean_shift,
float std_dev2,
float skewness) :
values_{mean, mean_shift, std_dev2, skewness}
{
}
void
Delay::operator=(float delay)
{
values_[0] = delay;
values_[1] = 0.0;
values_[2] = 0.0;
values_[3] = 0.0;
}
void
Delay::setValues(float mean,
float mean_shift,
float std_dev2,
float skewnes)
{
values_[0] = mean;
values_[1] = mean_shift;
values_[2] = std_dev2;
values_[3] = skewnes;
}
void
Delay::setMean(float mean)
{
values_[0] = mean;
}
void
Delay::setMeanShift(float mean_shift)
{
values_[1] = mean_shift;
}
float
Delay::stdDev() const
{
float std_dev2 = values_[2];
if (std_dev2 < 0.0)
// std_dev is negative for crpr to offset std_dev in the common
// clock path.
return -std::sqrt(-std_dev2);
else
return std::sqrt(std_dev2);
}
void
Delay::setStdDev(float std_dev)
{
values_[2] = square(std_dev);
}
void
Delay::setSkewness(float skewness)
{
values_[3] = skewness;
}
////////////////////////////////////////////////////////////////
DelayDbl::DelayDbl() :
values_{0.0, 0.0, 0.0, 0.0}
{
}
DelayDbl::DelayDbl(double mean) :
values_{mean, 0.0, 0.0, 0.0}
{
}
void
DelayDbl::setMean(double mean)
{
values_[0] = mean;
}
double
DelayDbl::stdDev() const
{
float std_dev2 = values_[2];
if (std_dev2 < 0.0)
// std_dev is negative for crpr to offset std_dev in the common
// clock path.
return -std::sqrt(-std_dev2);
else
return std::sqrt(std_dev2);
}
void
DelayDbl::setValues(double mean,
double mean_shift,
double std_dev2,
double skewnes)
{
values_[0] = mean;
values_[1] = mean_shift;
values_[2] = std_dev2;
values_[3] = skewnes;
}
void
DelayDbl::operator=(double delay)
{
values_[0] = delay;
values_[1] = 0.0;
values_[2] = 0.0;
values_[3] = 0.0;
}
////////////////////////////////////////////////////////////////
Delay
makeDelay(float mean,
float mean_shift,
float std_dev,
float skewness)
{
return Delay(mean, mean_shift, square(std_dev), skewness);
}
Delay
makeDelay(float mean,
float std_dev)
{
return Delay(mean, 0.0, square(std_dev), 0.0);
}
Delay
makeDelay2(float mean,
float std_dev)
{
return Delay(mean, 0.0, std_dev, 0.0);
}
void
delaySetMean(Delay &delay,
float mean)
{
delay.setMean(mean);
}
////////////////////////////////////////////////////////////////
Delay
delayDblAsDelay(DelayDbl &delay)
{
return Delay(delay.mean(), delay.meanShift(), delay.stdDev2(), delay.skewness());
}
std::string
delayAsString(const Delay &delay,
const StaState *sta)
{
return delayAsString(delay, EarlyLate::late(),
sta->units()->timeUnit()->digits(), sta);
}
std::string
delayAsString(const Delay &delay,
int digits,
const StaState *sta)
{
return delayAsString(delay, EarlyLate::late(), digits, sta);
}
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta)
{
return delayAsString(delay, early_late, sta->units()->timeUnit()->digits(), sta);
}
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
int digits,
const StaState *sta)
{
const Unit *unit = sta->units()->timeUnit();
float mean_std_dev = delayAsFloat(delay, early_late, sta);
return unit->asString(mean_std_dev, digits);
}
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
bool report_variance,
int digits,
const StaState *sta)
{
if (report_variance)
return sta->delayOps()->asStringVariance(delay, digits, sta);
else
return delayAsString(delay, early_late, digits, sta);
}
float
delayAsFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta)
{
return sta->delayOps()->asFloat(delay, early_late, sta);
}
float
delayAsFloat(const DelayDbl &delay,
const EarlyLate *early_late,
const StaState *sta)
{
return sta->delayOps()->asFloat(delay, early_late, sta);
}
float
delayAsFloat(const Delay &delay)
{
return delay.mean();
}
const Delay &
delayInitValue(const MinMax *min_max)
{
return delay_init_values[min_max->index()];
}
bool
delayIsInitValue(const Delay &delay,
const MinMax *min_max)
{
return fuzzyEqual(delay.mean(), min_max->initValue());
}
bool
delayZero(const Delay &delay,
const StaState *sta)
{
return sta->delayOps()->isZero(delay);
}
bool
delayInf(const Delay &delay,
const StaState *sta)
{
return sta->delayOps()->isInf(delay);
}
bool
delayEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->equal(delay1, delay2, sta);
}
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->less(delay1, delay2, sta);
}
bool
delayLess(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *sta)
{
return sta->delayOps()->less(delay1, delay2, sta);
}
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return sta->delayOps()->less(delay1, delay2, sta);
else
return sta->delayOps()->greater(delay1, delay2, sta);
}
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->lessEqual(delay1, delay2, sta);
}
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return sta->delayOps()->lessEqual(delay1, delay2, sta);
else
return sta->delayOps()->greaterEqual(delay1, delay2, sta);
}
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->greater(delay1, delay2, sta);
}
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return sta->delayOps()->greater(delay1, delay2, sta);
else
return sta->delayOps()->less(delay1, delay2, sta);
}
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->greaterEqual(delay1, delay2, sta);
}
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return sta->delayOps()->greaterEqual(delay1, delay2, sta);
else
return sta->delayOps()->lessEqual(delay1, delay2, sta);
}
Delay
delayRemove(const Delay &delay1,
const Delay &delay2)
{
return makeDelay2(delay1.mean() - delay2.mean(),
delay1.stdDev2() - delay2.stdDev2());
}
Delay
delaySum(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->sum(delay1, delay2);
}
Delay
delaySum(const Delay &delay1,
float delay2,
const StaState *sta)
{
return sta->delayOps()->sum(delay1, delay2);
}
Delay
delayDiff(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->diff(delay1, delay2);
}
Delay
delayDiff(const Delay &delay1,
float delay2,
const StaState *sta)
{
return sta->delayOps()->diff(delay1, delay2);
}
Delay
delayDiff(float delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->diff(delay1, delay2);
}
void
delayIncr(Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
sta->delayOps()->incr(delay1, delay2);
}
void
delayIncr(DelayDbl &delay1,
const Delay &delay2,
const StaState *sta)
{
sta->delayOps()->incr(delay1, delay2);
}
void
delayIncr(Delay &delay1,
float delay2,
const StaState *sta)
{
sta->delayOps()->incr(delay1, delay2);
}
void
delayDecr(Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
sta->delayOps()->decr(delay1, delay2);
}
void
delayDecr(DelayDbl &delay1,
const Delay &delay2,
const StaState *sta)
{
sta->delayOps()->decr(delay1, delay2);
}
Delay
delayProduct(const Delay &delay1,
float delay2,
const StaState *sta)
{
return sta->delayOps()->product(delay1, delay2);
}
Delay
delayDiv(float delay1,
const Delay &delay2,
const StaState *sta)
{
return sta->delayOps()->div(delay1, delay2);
}
float
delayStdDev2(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta)
{
return sta->delayOps()->stdDev2(delay, early_late);
}
} // namespace

View File

@ -131,12 +131,10 @@ proc set_delay_calculator { alg } {
if { [is_delay_calc_name $alg] } {
set_delay_calculator_cmd $alg
} else {
sta_error 195 "delay calculator $alg not found."
sta_error 2500 "delay calculator $alg not found."
}
}
define_cmd_args "set_pocv_sigma_factor" { factor }
################################################################
define_cmd_args "set_assigned_delay" \
@ -156,38 +154,38 @@ proc set_assigned_delay { args } {
if [info exists keys(-from)] {
set from_pins [get_port_pins_error "from_pins" $keys(-from)]
} else {
sta_error 196 "set_assigned_delay missing -from argument."
sta_error 2501 "set_assigned_delay missing -from argument."
}
if [info exists keys(-to)] {
set to_pins [get_port_pins_error "to_pins" $keys(-to)]
} else {
sta_error 182 "set_assigned_delay missing -to argument."
sta_error 2502 "set_assigned_delay missing -to argument."
}
set delay [lindex $args 0]
if {![string is double $delay]} {
sta_error 183 "set_assigned_delay delay is not a float."
sta_error 2503 "set_assigned_delay delay is not a float."
}
set delay [time_ui_sta $delay]
if {[info exists flags(-cell)] && [info exists flags(-net)]} {
sta_error 184 "set_annotated_delay -cell and -net options are mutually excluive."
sta_error 2504 "set_annotated_delay -cell and -net options are mutually excluive."
} elseif {[info exists flags(-cell)]} {
if { $from_pins != {} } {
set inst [[lindex $from_pins 0] instance]
foreach pin $from_pins {
if {[$pin instance] != $inst} {
sta_error 185 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]."
sta_error 2505 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]."
}
}
foreach pin $to_pins {
if {[$pin instance] != $inst} {
sta_error 186 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]"
sta_error 2506 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]"
}
}
}
} elseif {![info exists flags(-net)]} {
sta_error 187 "set_assigned_delay -cell or -net required."
sta_error 2508 "set_assigned_delay -cell or -net required."
}
foreach from_pin $from_pins {
set from_vertices [$from_pin vertices]
@ -231,7 +229,7 @@ proc set_assigned_delay2 {from_vertex to_vertex to_rf scene min_max delay} {
}
$edge_iter finish
if { !$matched } {
sta_error 193 "set_assigned_delay no timing arcs found between from/to pins."
sta_error 2509 "set_assigned_delay no timing arcs found between from/to pins."
}
}
@ -252,7 +250,7 @@ proc set_assigned_check { args } {
if { [info exists keys(-from)] } {
set from_pins [get_port_pins_error "from_pins" $keys(-from)]
} else {
sta_error 188 "set_assigned_check missing -from argument."
sta_error 2510 "set_assigned_check missing -from argument."
}
set from_rf "rise_fall"
if { [info exists keys(-clock)] } {
@ -261,14 +259,14 @@ proc set_assigned_check { args } {
|| $clk_arg eq "fall" } {
set from_rf $clk_arg
} else {
sta_error 189 "set_assigned_check -clock must be rise or fall."
sta_error 2511 "set_assigned_check -clock must be rise or fall."
}
}
if { [info exists keys(-to)] } {
set to_pins [get_port_pins_error "to_pins" $keys(-to)]
} else {
sta_error 190 "set_assigned_check missing -to argument."
sta_error 2512 "set_assigned_check missing -to argument."
}
set to_rf [parse_rise_fall_flags flags]
set scene [parse_scene keys]
@ -283,7 +281,7 @@ proc set_assigned_check { args } {
} elseif { [info exists flags(-removal)] } {
set role "removal"
} else {
sta_error 191 "set_assigned_check missing -setup|-hold|-recovery|-removal check type.."
sta_error 2513 "set_assigned_check missing -setup|-hold|-recovery|-removal check type.."
}
set cond ""
if { [info exists key(-cond)] } {
@ -291,7 +289,7 @@ proc set_assigned_check { args } {
}
set check_value [lindex $args 0]
if { ![string is double $check_value] } {
sta_error 192 "set_assigned_check check_value is not a float."
sta_error 2514 "set_assigned_check check_value is not a float."
}
set check_value [time_ui_sta $check_value]
@ -343,7 +341,7 @@ proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \
}
$edge_iter finish
if { !$matched } {
sta_error 194 "set_assigned_check no check arcs found between from/to pins."
sta_error 2516 "set_assigned_check no check arcs found between from/to pins."
}
}
@ -364,7 +362,7 @@ proc set_assigned_transition { args } {
set slew [lindex $args 0]
if {![string is double $slew]} {
sta_error 210 "set_assigned_transition transition is not a float."
sta_error 2518 "set_assigned_transition transition is not a float."
}
set slew [time_ui_sta $slew]
set pins [get_port_pins_error "pins" [lindex $args 1]]
@ -382,22 +380,31 @@ proc set_assigned_transition { args } {
################################################################
define_cmd_args "report_slews" {[-scenes scenes] pin}
define_cmd_args "report_slews" {[-scenes scenes] [-digits digits]\
[-report_variance] pin}
proc report_slews { args } {
global sta_report_default_digits
parse_key_args "report_slews" args keys {-corner -scenes} flags {}
parse_key_args "report_slews" args keys {-corner -scenes -digits} \
flags {-report_variance}
check_argc_eq1 "report_slews" $args
set scenes [parse_scenes_or_all keys]
set pin [get_port_pin_error "pin" [lindex $args 0]]
set digits $sta_report_default_digits
if [info exists keys(-digits)] {
set digits $keys(-digits)
check_positive_integer "-digits" $digits
} else {
set digits $sta_report_default_digits
}
set report_variance [info exists flags(-report_variance)]
foreach vertex [$pin vertices] {
set rise_min [format_time [$vertex slew_scenes rise $scenes min] $digits]
set rise_max [format_time [$vertex slew_scenes rise $scenes max] $digits]
set fall_min [format_time [$vertex slew_scenes fall $scenes min] $digits]
set fall_max [format_time [$vertex slew_scenes fall $scenes max] $digits]
set rise_min [$vertex slew_scenes_string rise $scenes min $report_variance $digits]
set rise_max [$vertex slew_scenes_string rise $scenes max $report_variance $digits]
set fall_min [$vertex slew_scenes_string fall $scenes min $report_variance $digits]
set fall_max [$vertex slew_scenes_string fall $scenes max $report_variance $digits]
report_line "[vertex_path_name $vertex] [rise_short_name] $rise_min:$rise_max [fall_short_name] $fall_min:$fall_max"
}
}

View File

@ -81,10 +81,10 @@ DelayCalcBase::finishDrvrPin()
void
DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin,
const RiseFall *rf,
Slew drvr_slew,
double drvr_slew,
float elmore,
ArcDelay &wire_delay,
Slew &load_slew)
double &wire_delay,
double &load_slew)
{
LibertyLibrary *load_library = thresholdLibrary(load_pin);
@ -107,8 +107,8 @@ void
DelayCalcBase::thresholdAdjust(const Pin *load_pin,
const LibertyLibrary *drvr_library,
const RiseFall *rf,
ArcDelay &load_delay,
Slew &load_slew)
double &wire_delay,
double &load_slew)
{
LibertyLibrary *load_library = thresholdLibrary(load_pin);
if (load_library
@ -118,11 +118,12 @@ DelayCalcBase::thresholdAdjust(const Pin *load_pin,
float load_vth = load_library->inputThreshold(rf);
float drvr_slew_delta = drvr_library->slewUpperThreshold(rf)
- drvr_library->slewLowerThreshold(rf);
float load_delay_delta =
float wire_delay_delta =
delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta);
load_delay += (rf == RiseFall::rise())
? load_delay_delta
: -load_delay_delta;
wire_delay += (rf == RiseFall::rise())
? wire_delay_delta
: -wire_delay_delta;
float load_slew_delta = load_library->slewUpperThreshold(rf)
- load_library->slewLowerThreshold(rf);
float drvr_slew_derate = drvr_library->slewDerateFromLibrary();
@ -162,9 +163,8 @@ DelayCalcBase::checkDelay(const Pin *check_pin,
float from_slew1 = delayAsFloat(from_slew);
float to_slew1 = delayAsFloat(to_slew);
return model->checkDelay(pinPvt(check_pin, scene, min_max),
from_slew1, to_slew1,
related_out_cap,
variables_->pocvEnabled());
from_slew1, to_slew1, related_out_cap,
min_max, variables_->pocvMode());
}
else
return delay_zero;
@ -187,8 +187,8 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin,
float to_slew1 = delayAsFloat(to_slew);
return model->reportCheckDelay(pinPvt(check_pin, scene, min_max),
from_slew1, from_slew_annotation,
to_slew1, related_out_cap, false,
digits);
to_slew1, related_out_cap, min_max,
PocvMode::scalar, digits);
}
return "";
}

View File

@ -72,16 +72,16 @@ protected:
void thresholdAdjust(const Pin *load_pin,
const LibertyLibrary *drvr_library,
const RiseFall *rf,
ArcDelay &load_delay,
Slew &load_slew);
double &load_delay,
double &load_slew);
// Helper function for input ports driving dspf parasitic.
void dspfWireDelaySlew(const Pin *load_pin,
const RiseFall *rf,
Slew drvr_slew,
double drvr_slew,
float elmore,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew);
double &wire_delay,
double &load_slew);
const Pvt *pinPvt(const Pin *pin,
const Scene *scene,
const MinMax *min_max);

232
dcalc/DelayNormal.cc Normal file
View File

@ -0,0 +1,232 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#include "DelayNormal.hh"
#include <cmath> // sqrt
#include "Error.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "Format.hh"
#include "StaState.hh"
#include "Variables.hh"
namespace sta {
float
DelayOpsNormal::stdDev2(const Delay &delay,
const EarlyLate *) const
{
return delay.stdDev2();
}
float
DelayOpsNormal::asFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta) const
{
float quantile = sta->variables()->pocvQuantile();
if (early_late == EarlyLate::early())
return delay.mean() - delay.stdDev() * quantile;
else // (early_late == EarlyLate::late())
return delay.mean() + delay.stdDev() * quantile;
}
double
DelayOpsNormal::asFloat(const DelayDbl &delay,
const EarlyLate *early_late,
const StaState *sta) const
{
double quantile = sta->variables()->pocvQuantile();
if (early_late == EarlyLate::early())
return delay.mean() - delay.stdDev() * quantile;
else // (early_late == EarlyLate::late())
return delay.mean() + delay.stdDev() * quantile;
}
bool
DelayOpsNormal::isZero(const Delay &delay) const
{
return fuzzyZero(delay.mean())
&& fuzzyZero(delay.stdDev2());
}
bool
DelayOpsNormal::isInf(const Delay &delay) const
{
return fuzzyInf(delay.mean());
}
bool
DelayOpsNormal::equal(const Delay &delay1,
const Delay &delay2,
const StaState *) const
{
return fuzzyEqual(delay1.mean(), delay2.mean())
&& fuzzyEqual(delay1.stdDev2(), delay2.stdDev2());
}
bool
DelayOpsNormal::less(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const
{
return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
DelayOpsNormal::less(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *sta) const
{
return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
DelayOpsNormal::lessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const
{
return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
DelayOpsNormal::greater(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const
{
return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
bool
DelayOpsNormal::greaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const
{
return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
Delay
DelayOpsNormal::sum(const Delay &delay1,
const Delay &delay2) const
{
return Delay(delay1.mean() + delay2.mean(),
delay1.stdDev2() + delay2.stdDev2());
}
Delay
DelayOpsNormal::sum(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() + delay2,
delay1.stdDev2());
}
Delay
DelayOpsNormal::diff(const Delay &delay1,
const Delay &delay2) const
{
return Delay(delay1.mean() - delay2.mean(),
delay1.stdDev2() + delay2.stdDev2());
}
Delay
DelayOpsNormal::diff(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() - delay2,
delay1.stdDev2());
}
Delay
DelayOpsNormal::diff(float delay1,
const Delay &delay2) const
{
return Delay(delay1 - delay2.mean(),
delay2.stdDev2());
}
void
DelayOpsNormal::incr(Delay &delay1,
const Delay &delay2) const
{
delay1.setValues(delay1.mean() + delay2.mean(), 0.0,
delay1.stdDev2() + delay2.stdDev2(), 0.0);
}
void
DelayOpsNormal::incr(DelayDbl &delay1,
const Delay &delay2) const
{
delay1.setValues(delay1.mean() + delay2.mean(), 0.0,
delay1.stdDev2() + delay2.stdDev2(), 0.0);
}
void
DelayOpsNormal::decr(Delay &delay1,
const Delay &delay2) const
{
delay1.setMean(delay1.mean() - delay2.mean());
}
void
DelayOpsNormal::decr(DelayDbl &delay1,
const Delay &delay2) const
{
delay1.setMean(delay1.mean() - delay2.mean());
}
Delay
DelayOpsNormal::product(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() * delay2,
delay1.stdDev2() * square(delay2));
}
Delay
DelayOpsNormal::div(float delay1,
const Delay &delay2) const
{
return Delay(delay1 / delay2.mean());
}
std::string
DelayOpsNormal::asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const
{
const Unit *unit = sta->units()->timeUnit();
return sta::format("{}[{}]",
unit->asString(delay.mean(), digits),
unit->asString(delay.stdDev(), digits));
}
} // namespace

206
dcalc/DelayScalar.cc Normal file
View File

@ -0,0 +1,206 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
// Delay as floats, non-SSTA.
#include "DelayScalar.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "StaState.hh"
namespace sta {
float
DelayOpsScalar::stdDev2(const Delay &,
const EarlyLate *) const
{
return 0.0;
}
float
DelayOpsScalar::asFloat(const Delay &delay,
const EarlyLate *,
const StaState *) const
{
return delay.mean();
}
double
DelayOpsScalar::asFloat(const DelayDbl &delay,
const EarlyLate *,
const StaState *) const
{
return delay.mean();
}
bool
DelayOpsScalar::isZero(const Delay &delay) const
{
return fuzzyZero(delay.mean());
}
bool
DelayOpsScalar::isInf(const Delay &delay) const
{
return fuzzyInf(delay.mean());
}
bool
DelayOpsScalar::equal(const Delay &delay1,
const Delay &delay2,
const StaState *) const
{
return fuzzyEqual(delay1.mean(), delay2.mean());
}
bool
DelayOpsScalar::less(const Delay &delay1,
const Delay &delay2,
const StaState *) const
{
return fuzzyLess(delay1.mean(), delay2.mean());
}
bool
DelayOpsScalar::less(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *) const
{
return fuzzyLess(delay1.mean(), delay2.mean());
}
bool
DelayOpsScalar::lessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *) const
{
return fuzzyLessEqual(delay1.mean(), delay2.mean());
}
bool
DelayOpsScalar::greater(const Delay &delay1,
const Delay &delay2,
const StaState *) const
{
return fuzzyGreater(delay1.mean(), delay2.mean());
}
bool
DelayOpsScalar::greaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *) const
{
return fuzzyGreaterEqual(delay1.mean(), delay2.mean());
}
Delay
DelayOpsScalar::sum(const Delay &delay1,
const Delay &delay2) const
{
return Delay(delay1.mean() + delay2.mean());
}
Delay
DelayOpsScalar::sum(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() + delay2);
}
Delay
DelayOpsScalar::diff(const Delay &delay1,
const Delay &delay2) const
{
return Delay(delay1.mean() - delay2.mean());
}
Delay
DelayOpsScalar::diff(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() - delay2);
}
Delay
DelayOpsScalar::diff(float delay1,
const Delay &delay2) const
{
return Delay(delay1 - delay2.mean());
}
void
DelayOpsScalar::incr(Delay &delay1,
const Delay &delay2) const
{
delay1.setMean(delay1.mean() + delay2.mean());
}
void
DelayOpsScalar::incr(DelayDbl &delay1,
const Delay &delay2) const
{
delay1.setMean(delay1.mean() + delay2.mean());
}
void
DelayOpsScalar::decr(Delay &delay1,
const Delay &delay2) const
{
delay1.setMean(delay1.mean() - delay2.mean());
}
void
DelayOpsScalar::decr(DelayDbl &delay1,
const Delay &delay2) const
{
delay1.setMean(delay1.mean() - delay2.mean());
}
Delay
DelayOpsScalar::product(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() * delay2);
}
Delay
DelayOpsScalar::div(float delay1,
const Delay &delay2) const
{
return Delay(delay1 / delay2.mean());
}
std::string
DelayOpsScalar::asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const
{
const Unit *unit = sta->units()->timeUnit();
return unit->asString(delay.mean(), digits);
}
} // namespace

293
dcalc/DelaySkewNormal.cc Normal file
View File

@ -0,0 +1,293 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#include "DelaySkewNormal.hh"
#include <cmath> // sqrt
#include "Error.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "Format.hh"
#include "StaState.hh"
#include "Variables.hh"
namespace sta {
float
DelayOpsSkewNormal::stdDev2(const Delay &delay,
const EarlyLate *) const
{
return delay.stdDev2();
}
float
DelayOpsSkewNormal::asFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta) const
{
// LVF: mean + mean_shift + sigma * sigma_factor with skewness consideration.
float quantile = sta->variables()->pocvQuantile();
if (early_late == EarlyLate::early())
return delay.mean() + delay.meanShift()
- delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0);
else // (early_late == EarlyLate::late())
return delay.mean() + delay.meanShift()
+ delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0);
}
double
DelayOpsSkewNormal::asFloat(const DelayDbl &delay,
const EarlyLate *early_late,
const StaState *sta) const
{
// LVF: mean + mean_shift + sigma * sigma_factor with skewness consideration.
double quantile = sta->variables()->pocvQuantile();
if (early_late == EarlyLate::early())
return delay.mean() + delay.meanShift()
- delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0);
else // (early_late == EarlyLate::late())
return delay.mean() + delay.meanShift()
+ delay.stdDev() * (quantile + delay.skewness() * (square(quantile)-1.0) / 6.0);
}
bool
DelayOpsSkewNormal::isZero(const Delay &delay) const
{
return fuzzyZero(delay.mean())
&& fuzzyZero(delay.meanShift())
&& fuzzyZero(delay.stdDev2())
&& fuzzyZero(delay.skewness());
}
bool
DelayOpsSkewNormal::isInf(const Delay &delay) const
{
return fuzzyInf(delay.mean());
}
bool
DelayOpsSkewNormal::equal(const Delay &delay1,
const Delay &delay2,
const StaState *) const
{
return fuzzyEqual(delay1.mean(), delay2.mean())
&& fuzzyEqual(delay1.meanShift(), delay2.meanShift())
&& fuzzyEqual(delay1.stdDev2(), delay2.stdDev2())
&& fuzzyEqual(delay1.skewness(), delay2.skewness());
}
bool
DelayOpsSkewNormal::less(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const
{
return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
DelayOpsSkewNormal::less(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *sta) const
{
return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
DelayOpsSkewNormal::lessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const
{
return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
DelayOpsSkewNormal::greater(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const
{
return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
bool
DelayOpsSkewNormal::greaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const
{
return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
Delay
DelayOpsSkewNormal::sum(const Delay &delay1,
const Delay &delay2) const
{
return Delay(delay1.mean() + delay2.mean(),
delay1.meanShift() + delay2.meanShift(),
delay1.stdDev2() + delay2.stdDev2(),
skewnessSum(delay1, delay2));
}
float
DelayOpsSkewNormal::skewnessSum(const Delay &delay1,
const Delay &delay2) const
{
return skewnessSum(delay1.stdDev(), delay1.skewness(),
delay2.stdDev(), delay2.skewness());
}
// Helper function to compute combined skewness from std dev and skewness values.
double
DelayOpsSkewNormal::skewnessSum(double std_dev1,
double skewness1,
double std_dev2,
double skewness2) const
{
double std_dev_sum = square(std_dev1) + square(std_dev2);
if (std_dev_sum == 0.0)
return 0.0;
else {
// Un-normalize the skews so they are third moments so they can be added.
double skew = (skewness1 * cube(std_dev1) + skewness2 * cube(std_dev2))
// std_dev_sum^(3/2)
/ (std_dev_sum * std::sqrt(std_dev_sum));
return skew;
}
}
Delay
DelayOpsSkewNormal::sum(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() + delay2,
delay1.meanShift(),
delay1.stdDev2(),
delay1.skewness());
}
Delay
DelayOpsSkewNormal::diff(const Delay &delay1,
const Delay &delay2) const
{
return Delay(delay1.mean() - delay2.mean(),
delay1.meanShift() - delay2.meanShift(),
delay1.stdDev2() + delay2.stdDev2(),
skewnessSum(delay1, delay2));
}
Delay
DelayOpsSkewNormal::diff(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() - delay2,
delay1.meanShift(),
delay1.stdDev2(),
delay1.skewness());
}
Delay
DelayOpsSkewNormal::diff(float delay1,
const Delay &delay2) const
{
return Delay(delay1 - delay2.mean(),
delay2.meanShift(),
delay2.stdDev2(),
delay2.skewness());
}
void
DelayOpsSkewNormal::incr(Delay &delay1,
const Delay &delay2) const
{
delay1.setValues(delay1.mean() + delay2.mean(),
delay1.meanShift() + delay2.meanShift(),
delay1.stdDev2() + delay2.stdDev2(),
skewnessSum(delay1, delay2));
}
void
DelayOpsSkewNormal::incr(DelayDbl &delay1,
const Delay &delay2) const
{
delay1.setValues(delay1.mean() + delay2.mean(),
delay1.meanShift() + delay2.meanShift(),
delay1.stdDev2() + delay2.stdDev2(),
skewnessSum(delay1.stdDev(), delay1.skewness()));
}
void
DelayOpsSkewNormal::decr(Delay &delay1,
const Delay &delay2) const
{
delay1.setValues(delay1.mean() - delay2.mean(),
delay1.meanShift() + delay2.meanShift(),
delay1.stdDev2() + delay2.stdDev2(),
skewnessSum(delay1, delay2));
}
void
DelayOpsSkewNormal::decr(DelayDbl &delay1,
const Delay &delay2) const
{
delay1.setValues(delay1.mean() - delay2.mean(),
delay1.meanShift() + delay2.meanShift(),
delay1.stdDev2() + delay2.stdDev2(),
skewnessSum(delay1.stdDev(), delay1.skewness()));
}
Delay
DelayOpsSkewNormal::product(const Delay &delay1,
float delay2) const
{
return Delay(delay1.mean() * delay2,
delay1.meanShift() * delay2,
delay1.stdDev2() * square(delay2),
delay1.skewness() * cube(delay2));
}
Delay
DelayOpsSkewNormal::div(float delay1,
const Delay &delay2) const
{
return Delay(delay1 / delay2.mean());
}
std::string
DelayOpsSkewNormal::asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const
{
const Unit *unit = sta->units()->timeUnit();
return sta::format("{}[{},{},{}]",
unit->asString(delay.mean(), digits),
unit->asString(delay.meanShift(), digits),
unit->asString(delay.stdDev(), digits),
sta->units()->scalarUnit()->asString(delay.skewness(), digits));
}
} // namespace

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
// "Performance Computation for Precharacterized CMOS Gates with RC Loads",
@ -36,6 +36,7 @@
#include <cmath>
#include <functional>
#include "Format.hh"
#include "Report.hh"
#include "Debug.hh"
#include "Units.hh"
@ -90,15 +91,14 @@ gateModelRd(const LibertyCell *cell,
double in_slew,
double c2,
double c1,
const Pvt *pvt,
bool pocv_enabled);
const Pvt *pvt);
static void
newtonRaphson(const int max_iter,
double x[],
const int n,
const double x_tol,
// eval(state) is called to fill fvec and fjac.
std::function<void ()> eval,
std::function<void()> eval,
// Temporaries supplied by caller.
double *fvec,
double **fjac,
@ -123,7 +123,8 @@ luDecomp(double **a,
class DmpAlg : public StaState
{
public:
DmpAlg(int nr_order, StaState *sta);
DmpAlg(int nr_order,
StaState *sta);
~DmpAlg() override = default;
virtual const char *name() = 0;
// Set driver model and pi model parameters for delay calculation.
@ -137,14 +138,14 @@ public:
double c2,
double rpi,
double c1);
virtual void gateDelaySlew(// Return values.
double &delay,
double &slew) = 0;
virtual void gateDelaySlew( // Return values.
double &delay,
double &slew) = 0;
virtual void loadDelaySlew(const Pin *load_pin,
double elmore,
// Return values.
ArcDelay &delay,
Slew &slew);
double &delay,
double &slew);
double ceff() { return ceff_; }
// Given x_ as a vector of input parameters, fill fvec_ with the
@ -189,9 +190,9 @@ protected:
void showX();
void showFvec();
void showJacobian();
void findDriverDelaySlew(// Return values.
double &delay,
double &slew);
void findDriverDelaySlew( // Return values.
double &delay,
double &slew);
double findVoCrossing(double vth,
double lower_bound,
double upper_bound);
@ -261,7 +262,7 @@ protected:
double fjac_storage_[max_nr_order_ * max_nr_order_];
double *fjac_[max_nr_order_];
double scale_[max_nr_order_];
double p_[max_nr_order_ ];
double p_[max_nr_order_];
int index_[max_nr_order_];
// Driver slew used to check load delay.
@ -275,7 +276,7 @@ protected:
};
DmpAlg::DmpAlg(int nr_order,
StaState *sta):
StaState *sta) :
StaState(sta),
c2_(0.0),
rpi_(0.0),
@ -329,14 +330,13 @@ DmpAlg::findDriverParams(double ceff)
double t0 = t_vth + std::log(1.0 - vth_) * rd_ * ceff - vth_ * dt;
x_[DmpParam::dt] = dt;
x_[DmpParam::t0] = t0;
newtonRaphson(100, x_, nr_order_, driver_param_tol,
[this] () { evalDmpEqns(); },
fvec_, fjac_, index_, p_, scale_);
newtonRaphson(
100, x_, nr_order_, driver_param_tol, [this]() { evalDmpEqns(); }, fvec_,
fjac_, index_, p_, scale_);
t0_ = x_[DmpParam::t0];
dt_ = x_[DmpParam::dt];
debugPrint(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s",
units_->timeUnit()->asString(t0_),
units_->timeUnit()->asString(dt_),
debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}",
units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_),
units_->capacitanceUnit()->asString(x_[DmpParam::ceff]));
if (debug_->check("dmp_ceff", 4))
showVo();
@ -348,13 +348,10 @@ DmpAlg::gateCapDelaySlew(double ceff,
double &delay,
double &slew)
{
ArcDelay model_delay;
Slew model_slew;
gate_model_->gateDelay(pvt_, in_slew_, ceff,
variables_->pocvEnabled(),
model_delay, model_slew);
delay = delayAsFloat(model_delay);
slew = delayAsFloat(model_slew);
float model_delay, model_slew;
gate_model_->gateDelay(pvt_, in_slew_, ceff, model_delay, model_slew);
delay = model_delay;
slew = model_slew;
}
void
@ -413,8 +410,7 @@ DmpAlg::dy(double t,
}
else {
dydt0 = -(y0dt(t1, cl) - y0dt(t1 - dt, cl)) / dt;
dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt)
+ y0dt(t1 - dt, cl) / dt;
dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) + y0dt(t1 - dt, cl) / dt;
dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt;
}
}
@ -437,14 +433,14 @@ void
DmpAlg::showX()
{
for (int i = 0; i < nr_order_; i++)
report_->reportLine("%4s %12.3e", dmp_param_index_strings[i], x_[i]);
report_->report("{:4} {:12.3e}", dmp_param_index_strings[i], x_[i]);
}
void
DmpAlg::showFvec()
{
for (int i = 0; i < nr_order_; i++)
report_->reportLine("%4s %12.3e", dmp_func_index_strings[i], fvec_[i]);
report_->report("{:4} {:12.3e}", dmp_func_index_strings[i], fvec_[i]);
}
void
@ -452,21 +448,21 @@ DmpAlg::showJacobian()
{
std::string line = " ";
for (int j = 0; j < nr_order_; j++)
line += stdstrPrint("%12s", dmp_param_index_strings[j]);
report_->reportLineString(line);
line.clear();
line += sta::format("{:>12}", dmp_param_index_strings[j]);
report_->reportLine(line);
for (int i = 0; i < nr_order_; i++) {
line += stdstrPrint("%4s ", dmp_func_index_strings[i]);
line.clear();
line += sta::format("{:4} ", dmp_func_index_strings[i]);
for (int j = 0; j < nr_order_; j++)
line += stdstrPrint("%12.3e ", fjac_[i][j]);
report_->reportLineString(line);
line += sta::format("{:12.3e} ", fjac_[i][j]);
report_->reportLine(line);
}
}
void
DmpAlg::findDriverDelaySlew(// Return values.
double &delay,
double &slew)
DmpAlg::findDriverDelaySlew( // Return values.
double &delay,
double &slew)
{
double t_upper = voCrossingUpperBound();
delay = findVoCrossing(vth_, t0_, t_upper);
@ -482,17 +478,15 @@ DmpAlg::findVoCrossing(double vth,
double t_lower,
double t_upper)
{
FindRootFunc vo_func = [&] (double t,
double &y,
double &dy) {
FindRootFunc vo_func = [&](double t, double &y, double &dy) {
double vo, vo_dt;
Vo(t, vo, vo_dt);
y = vo - vth;
dy = vo_dt;
};
bool fail;
double t_vth = findRoot(vo_func, t_lower, t_upper, vth_time_tol,
find_root_max_iter, fail);
double t_vth =
findRoot(vo_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail);
if (fail)
throw DmpError("find Vo crossing failed");
return t_vth;
@ -514,7 +508,7 @@ DmpAlg::Vo(double t,
V0(t1, v0, dv0_dt);
vo = v0 / dt_;
dvo_dt = dv0_dt / dt_;
dvo_dt = dv0_dt / dt_;
}
else {
double v0, dv0_dt;
@ -531,20 +525,20 @@ DmpAlg::Vo(double t,
void
DmpAlg::showVo()
{
report_->reportLine(" t vo(t)");
report_->report(" t vo(t)");
double ub = voCrossingUpperBound();
for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) {
double vo, dvo_dt;
Vo(t, vo, dvo_dt);
report_->reportLine(" %g %g", t, vo);
report_->report(" {:g} {:g}", t, vo);
}
}
void
DmpAlg::loadDelaySlew(const Pin *,
double elmore,
ArcDelay &delay,
Slew &slew)
double &delay,
double &slew)
{
if (!driver_valid_
|| elmore == 0.0
@ -557,10 +551,10 @@ DmpAlg::loadDelaySlew(const Pin *,
// Use the driver thresholds and rely on thresholdAdjust to
// convert the delay and slew to the load's thresholds.
try {
if (debug_->check("dmp_ceff", 4))
showVl();
elmore_ = elmore;
p3_ = 1.0 / elmore;
if (debug_->check("dmp_ceff", 4))
showVl();
double t_lower = t0_;
double t_upper = vlCrossingUpperBound();
double load_delay = findVlCrossing(vth_, t_lower, t_upper);
@ -585,8 +579,7 @@ DmpAlg::loadDelaySlew(const Pin *,
}
delay = delay1;
slew = slew1;
}
catch (DmpError &error) {
} catch (DmpError &error) {
fail(error.what());
delay = elmore_;
slew = drvr_slew_;
@ -600,17 +593,15 @@ DmpAlg::findVlCrossing(double vth,
double t_lower,
double t_upper)
{
FindRootFunc vl_func = [&] (double t,
double &y,
double &dy) {
FindRootFunc vl_func = [&](double t, double &y, double &dy) {
double vl, vl_dt;
Vl(t, vl, vl_dt);
y = vl - vth;
dy = vl_dt;
};
bool fail;
double t_vth = findRoot(vl_func, t_lower, t_upper, vth_time_tol,
find_root_max_iter, fail);
double t_vth =
findRoot(vl_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail);
if (fail)
throw DmpError("find Vl crossing failed");
return t_vth;
@ -654,12 +645,12 @@ DmpAlg::Vl(double t,
void
DmpAlg::showVl()
{
report_->reportLine(" t vl(t)");
report_->report(" t vl(t)");
double ub = vlCrossingUpperBound();
for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) {
double vl, dvl_dt;
Vl(t, vl, dvl_dt);
report_->reportLine(" %g %g", t, vl);
report_->report(" {:g} {:g}", t, vl);
}
}
@ -668,12 +659,11 @@ DmpAlg::fail(const char *reason)
{
// Report failures with a unique debug flag.
if (debug_->check("dmp_ceff", 1) || debug_->check("dcalc_error", 1))
report_->reportLine("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s",
reason,
units_->capacitanceUnit()->asString(c2_),
units_->resistanceUnit()->asString(rpi_),
units_->capacitanceUnit()->asString(c1_),
units_->resistanceUnit()->asString(rd_));
report_->report("delay_calc: DMP failed - {} c2={} rpi={} c1={} rd={}", reason,
units_->capacitanceUnit()->asString(c2_),
units_->resistanceUnit()->asString(rpi_),
units_->capacitanceUnit()->asString(c1_),
units_->resistanceUnit()->asString(rd_));
}
////////////////////////////////////////////////////////////////
@ -694,14 +684,14 @@ public:
double c2,
double rpi,
double c1) override;
void gateDelaySlew(// Return values.
double &delay,
double &slew) override;
void gateDelaySlew( // Return values.
double &delay,
double &slew) override;
void loadDelaySlew(const Pin *,
double elmore,
// Return values.
ArcDelay &delay,
Slew &slew) override;
double &delay,
double &slew) override;
void evalDmpEqns() override;
double voCrossingUpperBound() override;
@ -716,8 +706,9 @@ private:
double &dvl_dt) override;
};
DmpCap::DmpCap(StaState *sta):
DmpAlg(1, sta)
DmpCap::DmpCap(StaState *sta) :
DmpAlg(1,
sta)
{
}
@ -734,17 +725,17 @@ DmpCap::init(const LibertyLibrary *drvr_library,
double c1)
{
debugPrint(debug_, "dmp_ceff", 3, "Using DMP cap");
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf,
rd, in_slew, c2, rpi, c1);
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
c1);
ceff_ = c1 + c2;
}
void
DmpCap::gateDelaySlew(// Return values.
double &delay,
double &slew)
DmpCap::gateDelaySlew( // Return values.
double &delay,
double &slew)
{
debugPrint(debug_, "dmp_ceff", 3, " ceff = %s",
debugPrint(debug_, "dmp_ceff", 3, " ceff = {}",
units_->capacitanceUnit()->asString(ceff_));
gateCapDelaySlew(ceff_, delay, slew);
drvr_slew_ = slew;
@ -753,8 +744,8 @@ DmpCap::gateDelaySlew(// Return values.
void
DmpCap::loadDelaySlew(const Pin *,
double elmore,
ArcDelay &delay,
Slew &slew)
double &delay,
double &slew)
{
delay = elmore;
slew = drvr_slew_;
@ -782,7 +773,7 @@ DmpCap::voCrossingUpperBound()
}
void
DmpCap::Vl0(double ,
DmpCap::Vl0(double,
// Return values.
double &vl,
double &dvl_dt)
@ -809,9 +800,9 @@ public:
double c2,
double rpi,
double c1) override;
void gateDelaySlew(// Return values.
double &delay,
double &slew) override;
void gateDelaySlew( // Return values.
double &delay,
double &slew) override;
void evalDmpEqns() override;
double voCrossingUpperBound() override;
@ -847,7 +838,8 @@ private:
};
DmpPi::DmpPi(StaState *sta) :
DmpAlg(3, sta),
DmpAlg(3,
sta),
p1_(0.0),
p2_(0.0),
z1_(0.0),
@ -875,8 +867,8 @@ DmpPi::init(const LibertyLibrary *drvr_library,
double c1)
{
debugPrint(debug_, "dmp_ceff", 3, "Using DMP Pi");
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd,
in_slew, c2, rpi, c1);
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
c1);
// Find poles/zeros.
z1_ = 1.0 / (rpi_ * c1_);
@ -900,9 +892,9 @@ DmpPi::init(const LibertyLibrary *drvr_library,
}
void
DmpPi::gateDelaySlew(// Return values.
double &delay,
double &slew)
DmpPi::gateDelaySlew( // Return values.
double &delay,
double &slew)
{
driver_valid_ = false;
try {
@ -911,23 +903,21 @@ DmpPi::gateDelaySlew(// Return values.
double table_delay, table_slew;
gateCapDelaySlew(ceff_, table_delay, table_slew);
delay = table_delay;
//slew = table_slew;
// slew = table_slew;
try {
double vo_delay, vo_slew;
findDriverDelaySlew(vo_delay, vo_slew);
driver_valid_ = true;
// Save Vo delay to measure load wire delay waveform.
vo_delay_ = vo_delay;
//delay = vo_delay;
// delay = vo_delay;
slew = vo_slew;
}
catch (DmpError &error) {
} catch (DmpError &error) {
fail(error.what());
// Fall back to table slew.
slew = table_slew;
}
}
catch (DmpError &error) {
} catch (DmpError &error) {
fail(error.what());
// Driver calculation failed - use Ceff=c1+c2.
ceff_ = c1_ + c2_;
@ -941,8 +931,7 @@ DmpPi::findDriverParamsPi()
{
try {
findDriverParams(c2_ + c1_);
}
catch (DmpError &) {
} catch (DmpError &) {
findDriverParams(c2_);
}
}
@ -985,36 +974,33 @@ DmpPi::evalDmpEqns()
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);
(-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);
(2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff)))
/ (dt * dt);
dy(t_vl, t0, dt, ceff,
fjac_[DmpFunc::y20][DmpParam::t0],
fjac_[DmpFunc::y20][DmpParam::dt],
fjac_[DmpFunc::y20][DmpParam::ceff]);
dy(t_vl, t0, dt, ceff, fjac_[DmpFunc::y20][DmpParam::t0],
fjac_[DmpFunc::y20][DmpParam::dt], fjac_[DmpFunc::y20][DmpParam::ceff]);
dy(t_vth, t0, dt, ceff,
fjac_[DmpFunc::y50][DmpParam::t0],
fjac_[DmpFunc::y50][DmpParam::dt],
fjac_[DmpFunc::y50][DmpParam::ceff]);
dy(t_vth, t0, dt, ceff, fjac_[DmpFunc::y50][DmpParam::t0],
fjac_[DmpFunc::y50][DmpParam::dt], fjac_[DmpFunc::y50][DmpParam::ceff]);
if (debug_->check("dmp_ceff", 4)) {
showX();
showFvec();
showJacobian();
report_->reportLine(".................");
report_->report(".................");
}
}
// Eqn 13, Eqn 14.
double
DmpPi::ipiIceff(double, double dt,
DmpPi::ipiIceff(double,
double dt,
double ceff_time,
double ceff)
{
@ -1022,11 +1008,11 @@ DmpPi::ipiIceff(double, double dt,
double exp_p2_dt = exp2(-p2_ * ceff_time);
double exp_dt_rd_ceff = exp2(-ceff_time / (rd_ * ceff));
double ipi = (A_ * ceff_time + (B_ / p1_) * (1.0 - exp_p1_dt)
+ (D_ / p2_) * (1.0 - exp_p2_dt))
/ (rd_ * ceff_time * dt);
double iceff = (rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff)
* (1.0 - exp_dt_rd_ceff))
/ (rd_ * ceff_time * dt);
+ (D_ / p2_) * (1.0 - exp_p2_dt))
/ (rd_ * ceff_time * dt);
double iceff =
(rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff) * (1.0 - exp_dt_rd_ceff))
/ (rd_ * ceff_time * dt);
return ipi - iceff;
}
@ -1051,14 +1037,13 @@ DmpPi::Vl0(double t,
double D1 = k0_ * (k1_ - k2_ / p3_);
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
double D4 = -p3_ * k0_ * k4_ / (p2_ - p3_);
double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_)
+ p3_ * k4_ / (p2_ - p3_));
double D5 =
k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_) + p3_ * k4_ / (p2_ - p3_));
double exp_p1 = exp2(-p1_ * t);
double exp_p2 = exp2(-p2_ * t);
double exp_p3 = exp2(-p3_ * t);
vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3;
dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2
- D5 * p3_ * exp_p3;
dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 - D5 * p3_ * exp_p3;
}
double
@ -1080,7 +1065,8 @@ public:
};
DmpOnePole::DmpOnePole(StaState *sta) :
DmpAlg(2, sta)
DmpAlg(2,
sta)
{
}
@ -1104,19 +1090,15 @@ DmpOnePole::evalDmpEqns()
showFvec();
}
dy(t_vl, t0, dt, ceff_,
fjac_[DmpFunc::y20][DmpParam::t0],
fjac_[DmpFunc::y20][DmpParam::dt],
ignore2);
dy(t_vl, t0, dt, ceff_, fjac_[DmpFunc::y20][DmpParam::t0],
fjac_[DmpFunc::y20][DmpParam::dt], ignore2);
dy(t_vth, t0, dt, ceff_,
fjac_[DmpFunc::y50][DmpParam::t0],
fjac_[DmpFunc::y50][DmpParam::dt],
ignore2);
dy(t_vth, t0, dt, ceff_, fjac_[DmpFunc::y50][DmpParam::t0],
fjac_[DmpFunc::y50][DmpParam::dt], ignore2);
if (debug_->check("dmp_ceff", 4)) {
showJacobian();
report_->reportLine(".................");
report_->report(".................");
}
}
@ -1144,19 +1126,19 @@ public:
double c2,
double rpi,
double c1) override;
void gateDelaySlew(// Return values.
double &delay,
double &slew) override;
void gateDelaySlew( // Return values.
double &delay,
double &slew) override;
private:
void V0(double t,
// Return values.
double &vo,
double &dvo_dt) override;
void Vl0(double t,
// Return values.
double &vl,
double &dvl_dt) override;
void Vl0(double t,
// Return values.
double &vl,
double &dvl_dt) override;
double voCrossingUpperBound() override;
// Pole/zero.
@ -1193,8 +1175,8 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library,
double c1)
{
debugPrint(debug_, "dmp_ceff", 3, "Using DMP C2=0");
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd,
in_slew, c2, rpi, c1);
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
c1);
ceff_ = c1;
z1_ = 1.0 / (rpi_ * c1_);
@ -1217,8 +1199,7 @@ DmpZeroC2::gateDelaySlew(// Return values.
findDriverDelaySlew(delay, slew);
driver_valid_ = true;
vo_delay_ = delay;
}
catch (DmpError &error) {
} catch (DmpError &error) {
fail(error.what());
// Fall back to table slew.
driver_valid_ = false;
@ -1241,9 +1222,9 @@ DmpZeroC2::V0(double t,
void
DmpZeroC2::Vl0(double t,
// Return values.
double &vl,
double &dvl_dt)
// Return values.
double &vl,
double &dvl_dt)
{
double D1 = k0_ * (k1_ - k2_ / p3_);
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
@ -1271,7 +1252,7 @@ newtonRaphson(const int max_iter,
double x[],
const int size,
const double x_tol,
std::function<void ()> eval,
std::function<void()> eval,
// Temporaries supplied by caller.
double *fvec,
double **fjac,
@ -1498,32 +1479,48 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
parasitics_->piModel(parasitic, c2, rpi, c1);
if (std::isnan(c2) || std::isnan(c1) || std::isnan(rpi))
report_->error(1040, "parasitic Pi model has NaNs.");
setCeffAlgorithm(drvr_library, drvr_cell, pinPvt(drvr_pin, scene, min_max),
const Pvt *pvt = pinPvt(drvr_pin, scene, min_max);
setCeffAlgorithm(drvr_library, drvr_cell, pvt,
table_model, rf, in_slew1, c2, rpi, c1);
double gate_delay, drvr_slew;
gateDelaySlew(gate_delay, drvr_slew);
// Fill in pocv parameters.
double ceff = dmp_alg_->ceff();
ArcDelay gate_delay2(gate_delay);
Slew drvr_slew2(drvr_slew);
if (variables_->pocvEnabled())
table_model->gateDelayPocv(pvt, in_slew1, ceff, min_max,
variables_->pocvMode(),
gate_delay2, drvr_slew2);
ArcDcalcResult dcalc_result(load_pin_index_map.size());
dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew);
dcalc_result.setGateDelay(gate_delay2);
dcalc_result.setDrvrSlew(drvr_slew2);
for (const auto &[load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay;
Slew load_slew;
double wire_delay;
double load_slew;
loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic,
wire_delay, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay);
dcalc_result.setLoadSlew(load_idx, load_slew);
// Copy pocv params from driver.
ArcDelay wire_delay2(gate_delay2);
Slew load_slew2(drvr_slew2);
delaySetMean(wire_delay2, wire_delay);
delaySetMean(load_slew2, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay2);
dcalc_result.setLoadSlew(load_idx, load_slew2);
}
return dcalc_result;
}
else {
ArcDcalcResult dcalc_result =
LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
load_pin_index_map, scene, min_max);
if (parasitic
&& !unsuppored_model_warned_) {
LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
load_pin_index_map, scene, min_max);
if (parasitic && !unsuppored_model_warned_) {
unsuppored_model_warned_ = true;
report_->warn(1041, "cell %s delay model not supported on SPF parasitics by DMP delay calculator",
report_->warn(1041,
"cell {} delay model not supported on SPF parasitics by DMP "
"delay calculator",
drvr_cell->name());
}
return dcalc_result;
@ -1543,8 +1540,7 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library,
{
double rd = 0.0;
if (gate_model) {
rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1,
pvt, variables_->pocvEnabled());
rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, pvt);
// Zero Rd means the table is constant and thus independent of load cap.
if (rd < 1e-2
// Rpi is small compared to Rd, which makes the load capacitive.
@ -1560,16 +1556,15 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library,
}
else
dmp_alg_ = dmp_cap_;
dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model,
rf, rd, in_slew, c2, rpi, c1);
dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
c1);
debugPrint(debug_, "dmp_ceff", 3,
" DMP in_slew = %s c2 = %s rpi = %s c1 = %s Rd = %s (%s alg)",
" DMP in_slew = {} c2 = {} rpi = {} c1 = {} Rd = {} ({} alg)",
units_->timeUnit()->asString(in_slew),
units_->capacitanceUnit()->asString(c2),
units_->resistanceUnit()->asString(rpi),
units_->capacitanceUnit()->asString(c1),
units_->resistanceUnit()->asString(rd),
dmp_alg_->name());
units_->resistanceUnit()->asString(rd), dmp_alg_->name());
}
std::string
@ -1583,8 +1578,9 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
const MinMax *min_max,
int digits)
{
ArcDcalcResult dcalc_result = gateDelay(drvr_pin, arc, in_slew, load_cap,
parasitic, load_pin_index_map, scene, min_max);
ArcDcalcResult dcalc_result =
gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map,
scene, min_max);
GateTableModel *model = arc->gateTableModel(scene, min_max);
float c_eff = 0.0;
std::string result;
@ -1612,14 +1608,12 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
else
c_eff = load_cap;
if (model) {
const Unit *time_unit = units->timeUnit();
float in_slew1 = delayAsFloat(in_slew);
result += model->reportGateDelay(pinPvt(drvr_pin, scene, min_max),
in_slew1, c_eff,
variables_->pocvEnabled(), digits);
in_slew1, c_eff, min_max,
variables_->pocvMode(), digits);
result += "Driver waveform slew = ";
float drvr_slew = delayAsFloat(dcalc_result.drvrSlew());
result += time_unit->asString(drvr_slew, digits);
result += delayAsString(dcalc_result.drvrSlew(), min_max, digits, this);
result += '\n';
}
return result;
@ -1632,25 +1626,22 @@ gateModelRd(const LibertyCell *cell,
double in_slew,
double c2,
double c1,
const Pvt *pvt,
bool pocv_enabled)
const Pvt *pvt)
{
float cap1 = c1 + c2;
float cap2 = cap1 + 1e-15;
ArcDelay d1, d2;
Slew s1, s2;
gate_model->gateDelay(pvt, in_slew, cap1, pocv_enabled, d1, s1);
gate_model->gateDelay(pvt, in_slew, cap2, pocv_enabled, d2, s2);
float d1, d2, s1, s2;
gate_model->gateDelay(pvt, in_slew, cap1, d1, s1);
gate_model->gateDelay(pvt, in_slew, cap2, d2, s2);
double vth = cell->libertyLibrary()->outputThreshold(rf);
float rd = -std::log(vth) * std::abs(delayAsFloat(d1) - delayAsFloat(d2))
/ (cap2 - cap1);
float rd = -std::log(vth) * std::abs(d1 - d2) / (cap2 - cap1);
return rd;
}
void
DmpCeffDelayCalc::gateDelaySlew(// Return values.
double &delay,
double &slew)
DmpCeffDelayCalc::gateDelaySlew( // Return values.
double &delay,
double &slew)
{
dmp_alg_->gateDelaySlew(delay, slew);
}
@ -1658,8 +1649,8 @@ DmpCeffDelayCalc::gateDelaySlew(// Return values.
void
DmpCeffDelayCalc::loadDelaySlewElmore(const Pin *load_pin,
double elmore,
ArcDelay &delay,
Slew &slew)
double &delay,
double &slew)
{
if (dmp_alg_)
dmp_alg_->loadDelaySlew(load_pin, elmore, delay, slew);
@ -1678,7 +1669,7 @@ DmpCeffDelayCalc::copyState(const StaState *sta)
DmpError::DmpError(const char *what) :
what_(what)
{
//printf("DmpError %s\n", what);
// printf("DmpError %s\n", what);
}
// This saves about 2.5% in overall run time on designs with SPEF.
@ -1707,4 +1698,4 @@ exp2(double x)
}
}
} // namespace
} // namespace sta

View File

@ -69,15 +69,15 @@ protected:
const LibertyLibrary *drvr_library,
const Parasitic *parasitic,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew) = 0;
double &wire_delay,
double &load_slew) = 0;
void gateDelaySlew(// Return values.
double &delay,
double &slew);
void loadDelaySlewElmore(const Pin *load_pin,
double elmore,
ArcDelay &delay,
Slew &slew);
double &delay,
double &slew);
// Select the appropriate special case Dartu/Menezes/Pileggi algorithm.
void setCeffAlgorithm(const LibertyLibrary *library,
const LibertyCell *cell,

View File

@ -59,8 +59,8 @@ protected:
const LibertyLibrary *drvr_library,
const Parasitic *parasitic,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew) override;
double &wire_delay,
double &load_slew) override;
};
ArcDelayCalc *
@ -93,8 +93,8 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *,
ArcDcalcResult dcalc_result(load_pin_index_map.size());
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
for (auto [load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew;
double wire_delay = 0.0;
double load_slew = in_slew;
bool elmore_exists = false;
float elmore = 0.0;
if (parasitic)
@ -116,8 +116,8 @@ DmpCeffElmoreDelayCalc::loadDelaySlew(const Pin *load_pin,
const LibertyLibrary *drvr_library,
const Parasitic *parasitic,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew)
double &wire_delay,
double &load_slew)
{
wire_delay = 0.0;
load_slew = drvr_slew;
@ -143,14 +143,14 @@ public:
Parasitic *findParasitic(const Pin *drvr_pin,
const RiseFall *rf,
const Scene *scene,
const MinMax *min_max) override;
const MinMax *min_max) override;
ArcDcalcResult inputPortDelay(const Pin *port_pin,
float in_slew,
const RiseFall *rf,
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const Scene *scene,
const MinMax *min_max) override;
const MinMax *min_max) override;
ArcDcalcResult gateDelay(const Pin *drvr_pin,
const TimingArc *arc,
const Slew &in_slew,
@ -158,7 +158,7 @@ public:
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const Scene *scene,
const MinMax *min_max) override;
const MinMax *min_max) override;
private:
void loadDelaySlew(const Pin *load_pin,
@ -167,14 +167,14 @@ private:
const LibertyLibrary *drvr_library,
const Parasitic *parasitic,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew) override;
double &wire_delay,
double &load_slew) override;
void loadDelay(double drvr_slew,
Parasitic *pole_residue,
double p1,
double k1,
ArcDelay &wire_delay,
Slew &load_slew);
double &wire_delay,
double &load_slew);
float loadDelay(double vth,
double p1,
double p2,
@ -267,8 +267,8 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *,
{
const Parasitics *parasitics = scene->parasitics(min_max);
ArcDcalcResult dcalc_result(load_pin_index_map.size());
ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew;
double wire_delay = 0.0;
double load_slew = in_slew;
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
for (const auto [load_pin, load_idx] : load_pin_index_map) {
if (parasitics->isPiPoleResidue(parasitic)) {
@ -323,8 +323,8 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin,
const LibertyLibrary *drvr_library,
const Parasitic *parasitic,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew)
double &wire_delay,
double &load_slew)
{
parasitic_is_pole_residue_ = parasitics_->isPiPoleResidue(parasitic);
// Should handle PiElmore parasitic.
@ -362,12 +362,12 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew,
double p1,
double k1,
// Return values.
ArcDelay &wire_delay,
Slew &load_slew)
double &wire_delay,
double &load_slew)
{
ComplexFloat pole2, residue2;
parasitics_->poleResidue(pole_residue, 1, pole2, residue2);
if (!delayZero(drvr_slew)
if (!delayZero(drvr_slew, this)
&& pole2.imag() == 0.0
&& residue2.imag() == 0.0) {
double p2 = pole2.real();

View File

@ -251,8 +251,8 @@ GraphDelayCalc::delayInvalid(const Pin *pin)
void
GraphDelayCalc::delayInvalid(Vertex *vertex)
{
debugPrint(debug_, "delay_calc", 2, "delay invalid %s",
vertex->to_string(this).c_str());
debugPrint(debug_, "delay_calc", 2, "delay invalid {}",
vertex->to_string(this));
if (graph_ && incremental_) {
invalid_delays_.insert(vertex);
// Invalidate driver that triggers dcalc for multi-driver nets.
@ -340,7 +340,7 @@ GraphDelayCalc::findDelays(Level level)
if (arc_delay_calc_) {
Stats stats(debug_, report_);
int dcalc_count = 0;
debugPrint(debug_, "delay_calc", 1, "find delays to level %d", level);
debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level);
if (!delays_seeded_) {
iter_->clear();
seedRootSlews();
@ -368,7 +368,7 @@ GraphDelayCalc::findDelays(Level level)
delays_exist_ = true;
incremental_ = true;
debugPrint(debug_, "delay_calc", 1, "found %d delays", dcalc_count);
debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count);
stats.report("Delay calc");
}
}
@ -404,34 +404,34 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex,
ArcDelayCalc *arc_delay_calc)
{
const Pin *drvr_pin = drvr_vertex->pin();
debugPrint(debug_, "delay_calc", 2, "seed driver slew %s",
drvr_vertex->to_string(this).c_str());
debugPrint(debug_, "delay_calc", 2, "seed driver slew {}",
drvr_vertex->to_string(this));
for (const Scene *scene : scenes_) {
const Sdc *sdc = scene->sdc();
for (const MinMax *min_max : MinMax::range()) {
for (const RiseFall *rf : RiseFall::range()) {
InputDrive *drive = nullptr;
if (network_->isTopLevelPort(drvr_pin)) {
Port *port = network_->port(drvr_pin);
if (network_->isTopLevelPort(drvr_pin)) {
Port *port = network_->port(drvr_pin);
drive = sdc->findInputDrive(port);
}
if (drive) {
const LibertyCell *drvr_cell;
const LibertyPort *from_port, *to_port;
float *from_slews;
}
if (drive) {
const LibertyCell *drvr_cell;
const LibertyPort *from_port, *to_port;
float *from_slews;
drive->driveCell(rf, min_max, drvr_cell, from_port,
from_slews, to_port);
if (drvr_cell) {
if (from_port == nullptr)
from_port = driveCellDefaultFromPort(drvr_cell, to_port);
findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf,
from_slews, to_port);
if (drvr_cell) {
if (from_port == nullptr)
from_port = driveCellDefaultFromPort(drvr_cell, to_port);
findInputDriverDelay(drvr_cell, drvr_pin, drvr_vertex, rf,
from_port, from_slews, to_port, scene, min_max);
}
else
}
else
seedNoDrvrCellSlew(drvr_vertex, drvr_pin, rf, drive, scene, min_max,
arc_delay_calc);
}
else
arc_delay_calc);
}
else
seedNoDrvrSlew(drvr_vertex, drvr_pin, rf, scene, min_max, arc_delay_calc);
}
}
@ -504,7 +504,7 @@ GraphDelayCalc::seedNoDrvrSlew(Vertex *drvr_vertex,
ArcDelayCalc *arc_delay_calc)
{
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
Slew slew(default_slew);
Slew slew = default_slew;
// Top level bidirect driver uses load slew unless
// bidirect instance paths are disabled.
if (bidirectDrvrSlewFromLoad(drvr_pin)) {
@ -527,30 +527,30 @@ void
GraphDelayCalc::seedLoadSlew(Vertex *vertex)
{
const Pin *pin = vertex->pin();
debugPrint(debug_, "delay_calc", 2, "seed load slew %s",
vertex->to_string(this).c_str());
debugPrint(debug_, "delay_calc", 2, "seed load slew {}",
vertex->to_string(this));
initSlew(vertex);
for (const Scene *scene : scenes_) {
const Sdc *sdc = scene->sdc();
for (const MinMax *min_max : MinMax::range()) {
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
for (const RiseFall *rf : RiseFall::range()) {
for (const RiseFall *rf : RiseFall::range()) {
ClockSet *clks = sdc->findLeafPinClocks(pin);
if (!vertex->slewAnnotated(rf, min_max)) {
float slew = 0.0;
if (clks) {
float slew = 0.0;
if (clks) {
slew = min_max->initValue();
for (Clock *clk : *clks) {
float clk_slew = clk->slew(rf, min_max);
if (min_max->compare(clk_slew, slew))
slew = clk_slew;
}
}
graph_->setSlew(vertex, rf, ap_index, slew);
slew = clk_slew;
}
}
graph_->setSlew(vertex, rf, ap_index, slew);
}
}
}
}
}
}
// If a driving cell does not specify a -from_pin, the first port
@ -602,7 +602,7 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell,
const Scene *scene,
const MinMax *min_max)
{
debugPrint(debug_, "delay_calc", 2, " driver cell %s %s",
debugPrint(debug_, "delay_calc", 2, " driver cell {} {}",
drvr_cell->name(),
rf->shortName());
for (TimingArcSet *arc_set : drvr_cell->timingArcSets(from_port, to_port)) {
@ -627,12 +627,12 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin,
const Scene *scene,
const MinMax *min_max)
{
debugPrint(debug_, "delay_calc", 3, " %s %s -> %s %s (%s)",
debugPrint(debug_, "delay_calc", 3, " {} {} -> {} {} ({})",
arc->from()->name(),
arc->fromEdge()->to_string().c_str(),
arc->fromEdge()->to_string(),
arc->to()->name(),
arc->toEdge()->to_string().c_str(),
arc->role()->to_string().c_str());
arc->toEdge()->to_string(),
arc->role()->to_string());
const RiseFall *drvr_rf = arc->toEdge()->asRiseFall();
if (drvr_rf) {
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
@ -646,19 +646,19 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin,
ArcDcalcResult intrinsic_result =
arc_delay_calc_->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr,
load_pin_index_map, scene, min_max);
ArcDelay intrinsic_delay = intrinsic_result.gateDelay();
const ArcDelay &intrinsic_delay = intrinsic_result.gateDelay();
ArcDcalcResult gate_result = arc_delay_calc_->gateDelay(drvr_pin, arc,
Slew(from_slew), load_cap,
parasitic,
load_pin_index_map,
scene, min_max);
ArcDelay gate_delay = gate_result.gateDelay();
Slew gate_slew = gate_result.drvrSlew();
const ArcDelay &gate_delay = gate_result.gateDelay();
const Slew &gate_slew = gate_result.drvrSlew();
ArcDelay load_delay = gate_delay - intrinsic_delay;
const ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this);
debugPrint(debug_, "delay_calc", 3,
" gate delay = %s intrinsic = %s slew = %s",
" gate delay = {} intrinsic = {} slew = {}",
delayAsString(gate_delay, this),
delayAsString(intrinsic_delay, this),
delayAsString(gate_slew, this));
@ -681,8 +681,8 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex,
bool propagate)
{
const Pin *pin = vertex->pin();
debugPrint(debug_, "delay_calc", 2, "find delays %s (%s)",
vertex->to_string(this).c_str(),
debugPrint(debug_, "delay_calc", 2, "find delays {} ({})",
vertex->to_string(this),
network_->cellName(network_->instance(pin)));
if (vertex->isRoot())
seedRootSlew(vertex, arc_delay_calc);
@ -729,9 +729,8 @@ GraphDelayCalc::loadSlews(LoadPinIndexMap &load_pin_index_map)
Vertex *load_vertex = graph_->pinLoadVertex(pin);
SlewSeq &slews = load_slews[index];;
slews.resize(slew_count);
Slew *vertex_slews = load_vertex->slews();
for (size_t i = 0; i < slew_count; i++)
slews[i] = vertex_slews[i];
slews[i] = graph_->slew(load_vertex, i);
}
return load_slews;
}
@ -744,9 +743,9 @@ GraphDelayCalc::loadSlewsChanged(DrvrLoadSlews &load_slews_prev,
for (auto const [pin, index] : load_pin_index_map) {
Vertex *load_vertex = graph_->pinLoadVertex(pin);
SlewSeq &slews_prev = load_slews_prev[index];;
const Slew *slews = load_vertex->slews();
for (size_t i = 0; i < slew_count; i++) {
if (!delayEqual(slews[i], slews_prev[i]))
const Slew slew = graph_->slew(load_vertex, i);
if (!delayEqual(slew, slews_prev[i], this))
return true;
}
}
@ -886,7 +885,7 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex)
Vertex *drvr = edge->from(graph_);
const Pin *drvr_pin = drvr->pin();
if (isLeafDriver(drvr_pin, network_)) {
debugPrint(debug_, "delay_calc", 3, " %s",
debugPrint(debug_, "delay_calc", 3, " {}",
network_->pathName(drvr_pin));
multi_drvr_net_map_[drvr] = multi_drvr;
drvr_vertices.push_back(drvr);
@ -915,19 +914,18 @@ GraphDelayCalc::initLoadSlews(Vertex *drvr_vertex)
Edge *wire_edge = edge_iter.next();
if (wire_edge->isWire()) {
Vertex *load_vertex = wire_edge->to(graph_);
for (Scene *scene : scenes_) {
for (const MinMax *min_max : MinMax::range()) {
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
Slew slew_init_value(min_max->initValue());
for (const RiseFall *rf : RiseFall::range()) {
for (const RiseFall *rf : RiseFall::range()) {
if (!load_vertex->slewAnnotated(rf, min_max))
graph_->setSlew(load_vertex, rf, ap_index, slew_init_value);
graph_->setSlew(load_vertex, rf, ap_index, slew_init_value);
}
}
}
}
}
}
}
bool
@ -965,12 +963,12 @@ GraphDelayCalc::initRootSlews(Vertex *vertex)
for (Scene *scene : scenes_) {
for (const MinMax *min_max : MinMax::range()) {
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
for (const RiseFall *rf : RiseFall::range()) {
for (const RiseFall *rf : RiseFall::range()) {
if (!vertex->slewAnnotated(rf, min_max))
graph_->setSlew(vertex, rf, ap_index, default_slew);
graph_->setSlew(vertex, rf, ap_index, default_slew);
}
}
}
}
}
void
@ -979,7 +977,7 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge)
Vertex *drvr_vertex = edge->to(graph_);
const Pin *drvr_pin = drvr_vertex->pin();
Instance *drvr_inst = network_->instance(drvr_pin);
debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s",
debugPrint(debug_, "delay_calc", 2, "find latch D->Q {}",
sdc_network_->pathName(drvr_inst));
std::array<bool, RiseFall::index_count> delay_exists = {false, false};
LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex);
@ -1195,36 +1193,36 @@ GraphDelayCalc::annotateDelaysSlews(Edge *edge,
bool
GraphDelayCalc::annotateDelaySlew(Edge *edge,
const TimingArc *arc,
ArcDelay &gate_delay,
Slew &gate_slew,
const ArcDelay &gate_delay,
const Slew &gate_slew,
const Scene *scene,
const MinMax *min_max)
{
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
debugPrint(debug_, "delay_calc", 3,
" %s %s -> %s %s (%s) scene:%s/%s",
" {} {} -> {} {} ({}) scene:{}/{}",
arc->from()->name(),
arc->fromEdge()->to_string().c_str(),
arc->fromEdge()->to_string(),
arc->to()->name(),
arc->toEdge()->to_string().c_str(),
arc->role()->to_string().c_str(),
scene->name().c_str(),
min_max->to_string().c_str());
arc->toEdge()->to_string(),
arc->role()->to_string(),
scene->name(),
min_max->to_string());
debugPrint(debug_, "delay_calc", 3,
" gate delay = %s slew = %s",
" gate delay = {} slew = {}",
delayAsString(gate_delay, this),
delayAsString(gate_slew, this));
bool delay_changed = false;
Vertex *drvr_vertex = edge->to(graph_);
const RiseFall *drvr_rf = arc->toEdge()->asRiseFall();
// Merge slews.
const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index);
const Slew drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index);
if (delayGreater(gate_slew, drvr_slew, min_max, this)
&& !drvr_vertex->slewAnnotated(drvr_rf, min_max)
&& !edge->role()->isLatchDtoQ())
graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew);
if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) {
const ArcDelay &prev_gate_delay = graph_->arcDelay(edge,arc,ap_index);
const ArcDelay prev_gate_delay = graph_->arcDelay(edge,arc,ap_index);
float gate_delay1 = delayAsFloat(gate_delay);
float prev_gate_delay1 = delayAsFloat(prev_gate_delay);
if (prev_gate_delay1 == 0.0
@ -1258,23 +1256,23 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex,
Vertex *load_vertex = wire_edge->to(graph_);
Pin *load_pin = load_vertex->pin();
size_t load_idx = load_pin_index_map[load_pin];
ArcDelay wire_delay = dcalc_result.wireDelay(load_idx);
Slew load_slew = dcalc_result.loadSlew(load_idx);
const ArcDelay &wire_delay = dcalc_result.wireDelay(load_idx);
const Slew &load_slew = dcalc_result.loadSlew(load_idx);
debugPrint(debug_, "delay_calc", 3,
" %s load delay = %s slew = %s",
load_vertex->to_string(this).c_str(),
" {} load delay = {} slew = {}",
load_vertex->to_string(this),
delayAsString(wire_delay, this),
delayAsString(load_slew, this));
bool load_changed = false;
if (!load_vertex->slewAnnotated(drvr_rf, min_max)) {
if (drvr_vertex->slewAnnotated(drvr_rf, min_max)) {
// Copy the driver slew to the load if it is annotated.
const Slew &drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index);
const Slew drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index);
graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew);
load_changed = true;
}
else {
const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index);
const Slew slew = graph_->slew(load_vertex, drvr_rf, ap_index);
if (!merge
|| delayGreater(load_slew, slew, min_max, this)) {
graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew);
@ -1287,7 +1285,7 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex,
// annotate the same wire edges so they must be combined
// rather than set.
const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, ap_index);
Delay wire_delay_extra = extra_delay + wire_delay;
Delay wire_delay_extra = delaySum(extra_delay, wire_delay, this);
if (!merge
|| delayGreater(wire_delay_extra, delay, min_max, this)) {
graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay_extra);
@ -1582,7 +1580,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
TimingArcSet *arc_set = edge->timingArcSet();
const Pin *to_pin = to_vertex->pin();
Instance *inst = network_->instance(to_pin);
debugPrint(debug_, "delay_calc", 2, "find check %s %s -> %s",
debugPrint(debug_, "delay_calc", 2, "find check {} {} -> {}",
sdc_network_->pathName(inst),
network_->portName(from_vertex->pin()),
network_->portName(to_pin));
@ -1602,18 +1600,18 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) {
const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf,
scene, min_max);
const Slew &to_slew = graph_->slew(to_vertex, to_rf, ap_index);
const Slew to_slew = graph_->slew(to_vertex, to_rf, ap_index);
debugPrint(debug_, "delay_calc", 3,
" %s %s -> %s %s (%s) scene:%s/%s",
" {} {} -> {} {} ({}) scene:{}/{}",
arc_set->from()->name(),
arc->fromEdge()->to_string().c_str(),
arc->fromEdge()->to_string(),
arc_set->to()->name(),
arc->toEdge()->to_string().c_str(),
arc_set->role()->to_string().c_str(),
scene->name().c_str(),
min_max->to_string().c_str());
arc->toEdge()->to_string(),
arc_set->role()->to_string(),
scene->name(),
min_max->to_string());
debugPrint(debug_, "delay_calc", 3,
" from_slew = %s to_slew = %s",
" from_slew = {} to_slew = {}",
delayAsString(from_slew, this),
delayAsString(to_slew, this));
float related_out_cap = 0.0;
@ -1624,7 +1622,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
to_slew, related_out_cap,
scene, min_max);
debugPrint(debug_, "delay_calc", 3,
" check_delay = %s",
" check_delay = {}",
delayAsString(check_delay, this));
graph_->setArcDelay(edge, arc, ap_index, check_delay);
delay_changed = true;
@ -1684,7 +1682,7 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge,
if (role->isTimingCheck()) {
const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, scene, min_max);
DcalcAPIndex slew_index = scene->dcalcAnalysisPtIndex(min_max);
const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index);
const Slew to_slew = graph_->slew(to_vertex, to_rf, slew_index);
const ClkNetwork *clk_network = scene->mode()->clkNetwork();
bool from_ideal_clk = clk_network->isIdealClock(from_vertex);
const char *from_slew_annotation = from_ideal_clk ? " (ideal clock)" : nullptr;

View File

@ -118,7 +118,7 @@ LumpedCapDelayCalc::inputPortDelay(const Pin *,
const MinMax *)
{
const LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
return makeResult(drvr_library,rf, 0.0, in_slew, load_pin_index_map);
return makeResult(drvr_library,rf, delay_zero, in_slew, load_pin_index_map);
}
ArcDcalcResult
@ -133,22 +133,28 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin,
{
GateTimingModel *model = arc->gateModel(scene, min_max);
debugPrint(debug_, "delay_calc", 3,
" in_slew = %s load_cap = %s lumped",
" in_slew = {} load_cap = {} lumped",
delayAsString(in_slew, this),
units()->capacitanceUnit()->asString(load_cap));
const RiseFall *rf = arc->toEdge()->asRiseFall();
const LibertyLibrary *drvr_library = arc->to()->libertyLibrary();
if (model) {
ArcDelay gate_delay;
Slew drvr_slew;
float gate_delay, drvr_slew;
float in_slew1 = delayAsFloat(in_slew);
// NaNs cause seg faults during table lookup.
if (std::isnan(load_cap) || std::isnan(delayAsFloat(in_slew)))
if (std::isnan(load_cap) || std::isnan(in_slew.mean()))
report_->error(1350, "gate delay input variable is NaN");
model->gateDelay(pinPvt(drvr_pin, scene, min_max), in_slew1, load_cap,
variables_->pocvEnabled(),
gate_delay, drvr_slew);
return makeResult(drvr_library, rf, gate_delay, drvr_slew, load_pin_index_map);
const Pvt *pvt = pinPvt(drvr_pin, scene, min_max);
model->gateDelay(pvt, in_slew1, load_cap, gate_delay, drvr_slew);
// Fill in pocv parameters.
ArcDelay gate_delay2(gate_delay);
Slew drvr_slew2(drvr_slew);
if (variables_->pocvEnabled())
model->gateDelayPocv(pvt, in_slew1, load_cap, min_max, variables_->pocvMode(),
gate_delay2, drvr_slew2);
return makeResult(drvr_library, rf, gate_delay2, drvr_slew2, load_pin_index_map);
}
else
return makeResult(drvr_library, rf, delay_zero, delay_zero, load_pin_index_map);
@ -157,17 +163,18 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin,
ArcDcalcResult
LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library,
const RiseFall *rf,
ArcDelay gate_delay,
Slew drvr_slew,
const ArcDelay &gate_delay,
const Slew &drvr_slew,
const LoadPinIndexMap &load_pin_index_map)
{
ArcDcalcResult dcalc_result(load_pin_index_map.size());
dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew);
double drvr_slew1 = delayAsFloat(drvr_slew);
for (const auto [load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay = 0.0;
Slew load_slew = drvr_slew;
double wire_delay = 0.0;
double load_slew = drvr_slew1;
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay);
dcalc_result.setLoadSlew(load_idx, load_slew);
@ -190,7 +197,8 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin,
if (model) {
float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(check_pin, scene, min_max),
in_slew1, load_cap, false, digits);
in_slew1, load_cap, min_max,
PocvMode::scalar, digits);
}
return "";
}

View File

@ -74,8 +74,8 @@ public:
protected:
ArcDcalcResult makeResult(const LibertyLibrary *drvr_library,
const RiseFall *rf,
ArcDelay gate_delay,
Slew drvr_slew,
const ArcDelay &gate_delay,
const Slew &drvr_slew,
const LoadPinIndexMap &load_pin_index_map);
using ArcDelayCalc::reduceParasitic;

View File

@ -88,13 +88,13 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
load_pin_index_map, scene, min_max);
ArcDelay gate_delay = gate_result.gateDelay();
Slew drvr_slew = gate_result.drvrSlew();
ArcDelay load_delay = gate_delay - intrinsic_delay;
ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this);
load_delays[drvr_idx] = load_delay;
if (!delayZero(load_delay))
load_delay_sum += 1.0 / load_delay;
if (!delayZero(drvr_slew))
slew_sum += 1.0 / drvr_slew;
if (!delayZero(load_delay, this))
delayIncr(load_delay_sum, delayDiv(1.0, load_delay, this), this);
if (!delayZero(drvr_slew, this))
delayIncr(slew_sum, delayDiv(1.0, drvr_slew, this), this);
dcalc_result.setLoadCount(load_pin_index_map.size());
for (const auto &[load_pin, load_idx] : load_pin_index_map) {
@ -103,14 +103,16 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
}
}
ArcDelay gate_load_delay = delayZero(load_delay_sum)
ArcDelay gate_load_delay = delayZero(load_delay_sum, this)
? delay_zero
: 1.0 / load_delay_sum;
ArcDelay drvr_slew = delayZero(slew_sum) ? delay_zero : 1.0 / slew_sum;
: delayDiv(1.0, load_delay_sum, this);
ArcDelay drvr_slew = delayZero(slew_sum, this)
? delay_zero
: delayDiv(1.0, slew_sum, this);
for (size_t drvr_idx = 0; drvr_idx < drvr_count; drvr_idx++) {
ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx];
dcalc_result.setGateDelay(intrinsic_delays[drvr_idx] + gate_load_delay);
dcalc_result.setGateDelay(delaySum(intrinsic_delays[drvr_idx], gate_load_delay, this));
dcalc_result.setDrvrSlew(drvr_slew);
}
return dcalc_results;

View File

@ -1,30 +1,30 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "PrimaDelayCalc.hh"
#include <cmath> // abs
#include <cmath> // abs
#include "Debug.hh"
#include "Units.hh"
@ -38,15 +38,16 @@
#include "Parasitics.hh"
#include "GraphDelayCalc.hh"
#include "DmpDelayCalc.hh"
#include "Format.hh"
#include <Eigen/LU>
#include <Eigen/QR>
namespace sta {
using Eigen::SparseLU;
using Eigen::HouseholderQR;
using Eigen::ColPivHouseholderQR;
using Eigen::HouseholderQR;
using Eigen::SparseLU;
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
// McGraw-Hill, Inc. New York, NY.
@ -90,10 +91,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) :
{
}
PrimaDelayCalc::~PrimaDelayCalc()
{
delete table_dcalc_;
}
PrimaDelayCalc::~PrimaDelayCalc() { delete table_dcalc_; }
ArcDelayCalc *
PrimaDelayCalc::copy()
@ -130,8 +128,8 @@ PrimaDelayCalc::findParasitic(const Pin *drvr_pin,
bool has_wire_cap;
graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap,
fanout, has_wire_cap);
parasitic = parasitics->makeWireloadNetwork(drvr_pin, wireload,
fanout, scene, min_max);
parasitic =
parasitics->makeWireloadNetwork(drvr_pin, wireload, fanout, scene, min_max);
}
return parasitic;
}
@ -160,14 +158,14 @@ PrimaDelayCalc::inputPortDelay(const Pin *drvr_pin,
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
const Parasitic *pi_elmore = nullptr;
if (parasitic && parasitics->isParasiticNetwork(parasitic))
pi_elmore = parasitics->reduceToPiElmore(parasitic, drvr_pin, rf,
scene, min_max);
pi_elmore =
parasitics->reduceToPiElmore(parasitic, drvr_pin, rf, scene, min_max);
for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second;
ArcDelay wire_delay = 0.0;
Slew load_slew = in_slew;
double wire_delay = 0.0;
double load_slew = in_slew;
bool elmore_exists = false;
float elmore = 0.0;
if (pi_elmore)
@ -190,12 +188,13 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin,
const Parasitic *parasitic,
const LoadPinIndexMap &load_pin_index_map,
const Scene *scene,
const MinMax *min_max)
const MinMax *min_max)
{
ArcDcalcArgSeq dcalc_args;
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic);
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map,
scene, min_max);
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap,
parasitic);
ArcDcalcResultSeq dcalc_results =
gateDelays(dcalc_args, load_pin_index_map, scene, min_max);
return dcalc_results[0];
}
@ -229,15 +228,15 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
&& output_waveforms->slewAxis()->inBounds(in_slew)
&& output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) {
output_waveforms_[drvr_idx] = output_waveforms;
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
dcalc_arg.drvrCell()->name(),
debugPrint(debug_, "ccs_dcalc", 1, "{} {}", dcalc_arg.drvrCell()->name(),
drvr_rf_->shortName());
LibertyCell *drvr_cell = dcalc_arg.drvrCell();
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
bool vdd_exists;
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!vdd_exists)
report_->error(1720, "VDD not defined in library %s", drvr_library->name());
report_->error(1720, "VDD not defined in library {}",
drvr_library->name());
drvr_cell->ensureVoltageWaveforms(scenes_);
if (drvr_idx == 0) {
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
@ -268,13 +267,13 @@ PrimaDelayCalc::tableDcalcResults()
const Pin *drvr_pin = dcalc_arg.drvrPin();
if (drvr_pin) {
const RiseFall *rf = dcalc_arg.drvrEdge();
const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf,
scene_, min_max_);
const Parasitic *parasitic =
table_dcalc_->findParasitic(drvr_pin, rf, scene_, min_max_);
dcalc_arg.setParasitic(parasitic);
}
}
return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_,
scene_, min_max_);
return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, scene_,
min_max_);
}
void
@ -284,8 +283,7 @@ PrimaDelayCalc::simulate()
stampEqns();
setXinit();
if (prima_order_ > 0
&& node_count_ > prima_order_) {
if (prima_order_ > 0 && node_count_ > prima_order_) {
primaReduce();
simulate1(Gq_, Cq_, Bq_, xq_init_, Vq_, prima_order_);
}
@ -297,11 +295,11 @@ PrimaDelayCalc::simulate()
void
PrimaDelayCalc::simulate1(const MatrixSd &G,
const MatrixSd &C,
const Eigen::MatrixXd &B,
const Eigen::VectorXd &x_init,
const Eigen::MatrixXd &x_to_v,
const size_t order)
const MatrixSd &C,
const Eigen::MatrixXd &B,
const Eigen::VectorXd &x_init,
const Eigen::MatrixXd &x_to_v,
const size_t order)
{
Eigen::VectorXd x(order);
Eigen::VectorXd x_prev(order);
@ -315,7 +313,8 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
v_ = v_prev_ = x_to_v * x_init;
time_step_ = time_step_prev_ = timeStep();
debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this));
debugPrint(debug_, "ccs_dcalc", 1, "time step {}",
delayAsString(time_step_, this));
MatrixSd A(order, order);
A = G + (2.0 / time_step_) * C;
@ -336,8 +335,8 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
v_ = v_prev_ = x_to_v * x_init;
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
double time_begin = output_waveforms_[0]->voltageTime((*dcalc_args_)[0].inSlewFlt(),
ceff_[0], 0.0);
double time_begin = output_waveforms_[0]->voltageTime(
(*dcalc_args_)[0].inSlewFlt(), ceff_[0], 0.0);
// Limit in case load voltage waveforms don't get to final value.
double time_end = time_begin + maxTime();
@ -349,9 +348,9 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
rhs = B * u_ + (1.0 / time_step_) * C * (3.0 * x_prev - x_prev2);
x = A_solver.solve(rhs);
v_ = x_to_v * x;
const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[0];
debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s",
debugPrint(debug_, "ccs_dcalc", 3, "{} ceff {} VDrvr {:.4f} Idrvr {}",
delayAsString(time, this),
units_->capacitanceUnit()->asString(ceff_[0]),
voltage(dcalc_arg.drvrPin()),
@ -384,7 +383,7 @@ double
PrimaDelayCalc::maxTime()
{
return (*dcalc_args_)[0].inSlewFlt()
+ (driverResistance() + resistance_sum_) * load_cap_ * 4;
+ (driverResistance() + resistance_sum_) * load_cap_ * 4;
}
float
@ -429,9 +428,8 @@ PrimaDelayCalc::findNodeCount()
const Pin *pin = parasitics_->pin(node);
if (pin) {
pin_node_map_[pin] = node_idx;
debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu",
network_->pathName(pin),
node_idx);
debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}",
network_->pathName(pin), node_idx);
}
double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
node_capacitances_.push_back(cap);
@ -441,14 +439,12 @@ PrimaDelayCalc::findNodeCount()
for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) {
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
ParasiticNode *node1 = parasitics_->node1(capacitor);
if (node1
&& !parasitics_->isExternal(node1)) {
if (node1 && !parasitics_->isExternal(node1)) {
size_t node_idx = node_index_map_[node1];
node_capacitances_[node_idx] += cap;
}
ParasiticNode *node2 = parasitics_->node2(capacitor);
if (node2
&& !parasitics_->isExternal(node2)) {
if (node2 && !parasitics_->isExternal(node2)) {
size_t node_idx = node_index_map_[node2];
node_capacitances_[node_idx] += cap;
}
@ -496,9 +492,8 @@ PrimaDelayCalc::initCeffIdrvr()
const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx];
ceff_[drvr_idx] = load_cap_;
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
drvr_current_[drvr_idx] =
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(),
ceff_[drvr_idx], 0.0);
drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
dcalc_arg.inSlewFlt(), ceff_[drvr_idx], 0.0);
}
}
@ -617,8 +612,7 @@ PrimaDelayCalc::updateCeffIdrvr()
double v2 = voltagePrev(node_idx);
double dv = v1 - v2;
if (drvr_rf_ == RiseFall::rise()) {
if (drvr_current != 0.0
&& dv > 0.0) {
if (drvr_current != 0.0 && dv > 0.0) {
double ceff = drvr_current * time_step_ / dv;
if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff))
ceff_[drvr_idx] = ceff;
@ -627,13 +621,11 @@ PrimaDelayCalc::updateCeffIdrvr()
// Whoa partner. Head'n for the weeds.
drvr_current_[drvr_idx] = 0.0;
else
drvr_current_[drvr_idx] =
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(),
ceff_[drvr_idx], v1);
drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
dcalc_arg.inSlewFlt(), ceff_[drvr_idx], v1);
}
else {
if (drvr_current != 0.0
&& dv < 0.0) {
if (drvr_current != 0.0 && dv < 0.0) {
double ceff = drvr_current * time_step_ / dv;
if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff))
ceff_[drvr_idx] = ceff;
@ -643,10 +635,8 @@ PrimaDelayCalc::updateCeffIdrvr()
drvr_current_[drvr_idx] = 0.0;
}
else
drvr_current_[drvr_idx] =
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(),
ceff_[drvr_idx],
vdd_ - v1);
drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
dcalc_arg.inSlewFlt(), ceff_[drvr_idx], vdd_ - v1);
}
}
}
@ -657,10 +647,8 @@ PrimaDelayCalc::loadWaveformsFinished()
for (auto pin_node : pin_node_map_) {
size_t node_idx = pin_node.second;
double v = voltage(node_idx);
if ((drvr_rf_ == RiseFall::rise()
&& v < vh_ + (vdd_ - vh_) * .5)
|| (drvr_rf_ == RiseFall::fall()
&& (v > vl_ * .5))) {
if ((drvr_rf_ == RiseFall::rise() && v < vh_ + (vdd_ - vh_) * .5)
|| (drvr_rf_ == RiseFall::fall() && (v > vl_ * .5))) {
return false;
}
}
@ -678,12 +666,10 @@ PrimaDelayCalc::measureThresholds(double time)
double v_prev = voltagePrev(node_idx);
for (size_t m = 0; m < measure_threshold_count_; m++) {
double th = measure_thresholds_[m];
if ((v_prev < th && th <= v)
|| (v_prev > th && th >= v)) {
double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s",
node_idx,
th,
if ((v_prev < th && th <= v) || (v_prev > th && th >= v)) {
double t_cross =
time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
debugPrint(debug_, "ccs_measure", 1, "node {} cross {:.2f} {}", node_idx, th,
delayAsString(t_cross, this));
threshold_times_[node_idx][m] = t_cross;
}
@ -722,14 +708,12 @@ PrimaDelayCalc::dcalcResults()
size_t drvr_node = pin_node_map_[drvr_pin];
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
float ref_time = output_waveforms_[drvr_idx]->referenceTime(dcalc_arg.inSlewFlt());
ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time;
Slew drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]);
double gate_delay = drvr_times[threshold_vth] - ref_time;
double drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]);
dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew);
debugPrint(debug_, "ccs_dcalc", 2,
"%s gate delay %s slew %s",
network_->pathName(drvr_pin),
delayAsString(gate_delay, this),
debugPrint(debug_, "ccs_dcalc", 2, "{} gate delay {} slew {}",
network_->pathName(drvr_pin), delayAsString(gate_delay, this),
delayAsString(drvr_slew, this));
dcalc_result.setLoadCount(load_pin_index_map_->size());
@ -739,10 +723,9 @@ PrimaDelayCalc::dcalcResults()
size_t load_node = pin_node_map_[load_pin];
ThresholdTimes &wire_times = threshold_times_[load_node];
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
Slew load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
debugPrint(debug_, "ccs_dcalc", 2,
"load %s %s delay %s slew %s",
double wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
double load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
debugPrint(debug_, "ccs_dcalc", 2, "load {} {} delay {} slew {}",
network_->pathName(load_pin),
drvr_rf_->shortName(),
delayAsString(wire_delay, this),
@ -849,16 +832,18 @@ PrimaDelayCalc::primaReduce2()
// Modified Gram-Schmidt orthonormalization
for (size_t j = 0; j < k; j++) {
Eigen::MatrixXd H = Vq.block(0, j * port_count_, order_, port_count_).transpose()
* Vq.block(0, k * port_count_, order_, port_count_);
Eigen::MatrixXd H =
Vq.block(0, j * port_count_, order_, port_count_).transpose()
* Vq.block(0, k * port_count_, order_, port_count_);
Vq.block(0, k * port_count_, order_, port_count_) =
Vq.block(0, k * port_count_, order_, port_count_) - Vq.block(0, j * port_count_, order_, port_count_) * H;
Vq.block(0, k * port_count_, order_, port_count_)
- Vq.block(0, j * port_count_, order_, port_count_) * H;
}
Eigen::MatrixXd Vq_k = Vq.block(0, k * port_count_, order_, port_count_);
Eigen::HouseholderQR<Eigen::MatrixXd> Vq_k_solver(Vq_k);
Eigen::MatrixXd VqQ = Vq_k_solver.householderQ();
Vq.block(0, k * port_count_, order_, port_count_) =
VqQ.block(0, 0, order_, port_count_);
Vq.block(0, k * port_count_, order_, port_count_) =
VqQ.block(0, 0, order_, port_count_);
}
Vq_.resize(order_, prima_order_);
Vq_ = Vq.block(0, 0, order_, prima_order_);
@ -920,7 +905,8 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin,
if (model) {
float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(drvr_pin, scene, min_max),
in_slew1, load_cap, false, digits);
in_slew1, load_cap, min_max,
PocvMode::scalar, digits);
}
return "";
}
@ -956,8 +942,8 @@ Waveform
PrimaDelayCalc::watchWaveform(const Pin *pin)
{
FloatSeq &voltages = watch_pin_values_[pin];
TableAxisPtr time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
FloatSeq(times_));
TableAxisPtr time_axis =
std::make_shared<TableAxis>(TableAxisVariable::time, FloatSeq(times_));
Table waveform(new FloatSeq(voltages), time_axis);
return waveform;
}
@ -968,7 +954,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name,
MatrixSd &matrix)
{
report_->reportLine("%s", name);
report_->report("{}", name);
reportMatrix(matrix);
}
@ -976,7 +962,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name,
Eigen::MatrixXd &matrix)
{
report_->reportLine("%s", name);
report_->report("{}", name);
reportMatrix(matrix);
}
@ -984,7 +970,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name,
Eigen::VectorXd &matrix)
{
report_->reportLine("%s", name);
report_->report("{}", name);
reportMatrix(matrix);
}
@ -992,22 +978,19 @@ void
PrimaDelayCalc::reportVector(const char *name,
std::vector<double> &matrix)
{
report_->reportLine("%s", name);
report_->report("{}", name);
reportVector(matrix);
}
void
PrimaDelayCalc::reportMatrix(MatrixSd &matrix)
{
for (Eigen::Index i = 0; i < matrix.rows(); i++) {
std::string line = "| ";
for (Eigen::Index j = 0; j < matrix.cols(); j++) {
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
line += entry;
line += " ";
}
for (Eigen::Index j = 0; j < matrix.cols(); j++)
line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " ";
line += "|";
report_->reportLineString(line);
report_->reportLine(line);
}
}
@ -1016,13 +999,10 @@ PrimaDelayCalc::reportMatrix(Eigen::MatrixXd &matrix)
{
for (Eigen::Index i = 0; i < matrix.rows(); i++) {
std::string line = "| ";
for (Eigen::Index j = 0; j < matrix.cols(); j++) {
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
line += entry;
line += " ";
}
for (Eigen::Index j = 0; j < matrix.cols(); j++)
line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " ";
line += "|";
report_->reportLineString(line);
report_->reportLine(line);
}
}
@ -1031,25 +1011,21 @@ PrimaDelayCalc::reportMatrix(Eigen::VectorXd &matrix)
{
std::string line = "| ";
for (Eigen::Index i = 0; i < matrix.rows(); i++) {
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i));
line += entry;
line += " ";
std::string entry =
line += sta::format("{:10.3e}", matrix.coeff(i)) + " ";
}
line += "|";
report_->reportLineString(line);
report_->reportLine(line);
}
void
PrimaDelayCalc::reportVector(std::vector<double> &matrix)
{
std::string line = "| ";
for (size_t i = 0; i < matrix.size(); i++) {
std::string entry = stdstrPrint("%10.3e", matrix[i]);
line += entry;
line += " ";
}
for (size_t i = 0; i < matrix.size(); i++)
line += sta::format("{:10.3e}", matrix[i]) + " ";
line += "|";
report_->reportLineString(line);
report_->reportLine(line);
}
} // namespace
} // namespace sta

View File

@ -24,6 +24,19 @@
This file summarizes STA API changes for each release.
2026/03/12
----------
The Report class used for reporting and error messages now uses std::format
instead of printf.
sta::format is a wrapper for std::format that will compile on gcc8, which
centos7 uses and does not support std::format.
stdstrPrint, strintPrint, stringAppend have been removed. Use sta::format.
reportLineString is now reportLine
Release 3.0.0 2025/01/03
------------------------
@ -62,6 +75,8 @@ The Vector/Map/Set/UnorderedSet classes have been removed and replaced by
the std containers. The member functions are now templated functions found
in ContainerHelpers.hh.
The Graph slew_rf_count option is no longer supported.
Release 2.6.2 2025/03/30
------------------------

View File

@ -2,6 +2,142 @@ OpenSTA Timing Analyzer Release Notes
-------------------------------------
This file summarizes user visible changes for each release.
See ApiChangeLog.txt for changes to the STA api.
2026/03/23
----------
The write_path_spice command -spice_directory has been changed to
-spice_file, which is a prefix for the spice filenames. Successive
paths are written in files name <spice_file>_<path_number>.sp.
Release 3.0.1 2026/03/12
------------------------
Statistical timing (SSTA) with Liberty LVF (Liberty Variation Format)
models is now supported. Statistical timing uses a probaility
distribution to represent a delay or slew ranther than a single
number.
Normal and skew normal probability distributions are supported.
SSTA is enabled with the sta_pocv_mode variaable.
set sta_pocv_mode scalar|normal|skew_normal
scalar mode is for non-SSTA analysis
normal mode uses gaussian normal distributions
skew_normal'mode is for skew normal LVF moment based distributions
The target quantile of a delay probability distribution (confidence level) is
set with the sta_pocv_quantile variable.
sta_pocv_quantile <float>
The default value is 3 standard deviations, or sigma.
Use the variance field with report_checks or report_check_types to see
distribution parameters in timing reports.
A command file for analyzing a design with statisical timing with an
LVF library is shown below.
read_liberty lvf_library.lib.gz
read_verilog design.v
link_design top
create_clock -period 50 clk
set_input_delay -clock clk 1 {in1 in2}
set sta_pocv_mode skew_normal
report_checks -fields {slew variation input_pin variation} -digits 3
Startpoint: r2 (rising edge-triggered flip-flop clocked by clk)
Endpoint: r3 (rising edge-triggered flip-flop clocked by clk)
Path Group: clk
Path Type: max
Slew Delay Variation Time Description
---------------------------------------------------------------------------
0.000 0.000 0.000 clock clk (rise edge)
0.000 0.000 clock network delay (ideal)
0.000 0.000 0.000 ^ r2/CK (FDPQ1)
12.026 mean
0.017 mean_shift
0.366 std_dev
0.000 skewness
4.648 12.409 12.409 v r2/Q (FFQ1)
4.648 0.000 12.409 v u1/A (BUF1)
6.084 mean
0.007 mean_shift
0.188 std_dev
0.000 skewness
2.513 6.137 18.546 v u1/X (BUF1)
2.513 0.000 18.546 v u2/A2 (AN21)
6.447 mean
0.008 mean_shift
0.191 std_dev
0.000 skewness
2.565 6.497 25.043 v u2/X (AN21)
2.565 0.000 25.043 v r3/D (FFQ1)
25.043 data arrival time
0.000 50.000 50.000 clock clk (rise edge)
0.000 50.000 clock network delay (ideal)
0.000 50.000 clock reconvergence pessimism
50.000 ^ r3/CK (FFQ1)
-9.376 40.624 library setup time
40.624 data required time
---------------------------------------------------------------------------
40.624 data required time
-25.043 data arrival time
---------------------------------------------------------------------------
15.581 slack (MET)
The following commands now support a -report_variance arggument.
report_arrival [-report_variance]
report_required [-report_variance]
report_slack [-report_variance]
report_slews [-report_variance]
report_edges [-report_variance]
The following commands now support a -digits option.
report_edges [-digits digits]
report_slews [-digits digits]
The standard deviation for normal distributions is specified with the
following liberty timing groups.
ocv_sigma_cell_rise
ocv_sigma_cell_fall
ocv_sigma_rise_transition
ocv_sigma_fall_transition
ocv_sigma_rise_constraint
ocv_sigma_fall_constraint
LVF skew normal distributions are specified with liberty groups below.
ocv_std_dev_cell_rise
ocv_std_dev_cell_fall
ocv_mean_shift_cell_rise
ocv_mean_shift_cell_fall
ocv_skewness_cell_rise
ocv_skewness_cell_fall
ocv_std_dev_rise_transition
ocv_std_dev_fall_transition
ocv_skewness_rise_transition
ocv_skewness_fall_transition
ocv_mean_shift_rise_transition
ocv_mean_shift_fall_transition
ocv_std_dev_rise_constraint
ocv_std_dev_fall_constraint
ocv_skewness_rise_constraint
ocv_skewness_fall_constraint
ocv_mean_shift_rise_constraint
ocv_mean_shift_fall_constraint
2026/02/24
----------

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -323,28 +323,6 @@ net have wire edges between the pin vertices. Timing arc sets in the
leaf instance timing models have corresponding edges in the graph
between pins on the instance.
The Graph class constructor option slew_tr_count is used to prevent
the grpah from reserving memory to store slews. Similarly, if the
have_arc_delays option is false no memory is reserved for storing arc
delay values. This is useful if an external delay calculator is used
to annotate delays on the graph. In this case the Graph functions
arcDelay and wireDelay should be overloaded to return delay values
stored outside of the STA.
A graph with no slews or delays is constructed using:
Graph(this, 0, false, ap_count);
A graph with one slew for rising and falling edges is constructed using:
Graph(this, 1, true, ap_count);
A graph with separate rising and falling slews (the default) is
constructed using:
Graph(this, 2, true, ap_count);
SDC
---

View File

@ -61,7 +61,7 @@ foreach subdir $subdirs {
set files [glob -nocomplain [file join $subdir "*.{cc,hh,yy,ll,i}"]]
set files_c [concat $files_c $files]
}
set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|criticalError|libWarn|libError)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")}
set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|criticalError|warn|error)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")}
set files_tcl {}
foreach subdir $subdirs {

View File

@ -1,199 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#include "Delay.hh"
#include "StaConfig.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "StaState.hh"
// Non-SSTA compilation.
#if !SSTA
namespace sta {
static Delay delay_init_values[MinMax::index_count];
void
initDelayConstants()
{
delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue();
delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue();
}
const char *
delayAsString(const Delay &delay,
const StaState *sta)
{
return delayAsString(delay, sta, sta->units()->timeUnit()->digits());
}
const char *
delayAsString(const Delay &delay,
const StaState *sta,
int digits)
{
return sta->units()->timeUnit()->asString(delay, digits);
}
const char *
delayAsString(const Delay &delay,
const EarlyLate *,
const StaState *sta,
int digits)
{
const Unit *unit = sta->units()->timeUnit();
return unit->asString(delay, digits);
}
const Delay &
delayInitValue(const MinMax *min_max)
{
return delay_init_values[min_max->index()];
}
bool
delayIsInitValue(const Delay &delay,
const MinMax *min_max)
{
return fuzzyEqual(delay, min_max->initValue());
}
bool
delayZero(const Delay &delay)
{
return fuzzyZero(delay);
}
bool
delayInf(const Delay &delay)
{
return fuzzyInf(delay);
}
bool
delayEqual(const Delay &delay1,
const Delay &delay2)
{
return fuzzyEqual(delay1, delay2);
}
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const StaState *)
{
return fuzzyLess(delay1, delay2);
}
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *)
{
if (min_max == MinMax::max())
return fuzzyLess(delay1, delay2);
else
return fuzzyGreater(delay1, delay2);
}
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *)
{
return fuzzyLessEqual(delay1, delay2);
}
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *)
{
if (min_max == MinMax::max())
return fuzzyLessEqual(delay1, delay2);
else
return fuzzyGreaterEqual(delay1, delay2);
}
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const StaState *)
{
return fuzzyGreater(delay1, delay2);
}
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *)
{
if (min_max == MinMax::max())
return fuzzyGreater(delay1, delay2);
else
return fuzzyLess(delay1, delay2);
}
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *)
{
return fuzzyGreaterEqual(delay1, delay2);
}
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *)
{
if (min_max == MinMax::max())
return fuzzyGreaterEqual(delay1, delay2);
else
return fuzzyLessEqual(delay1, delay2);
}
Delay
delayRemove(const Delay &delay1,
const Delay &delay2)
{
return delay1 - delay2;
}
float
delayRatio(const Delay &delay1,
const Delay &delay2)
{
return delay1 / delay2;
}
} // namespace
#endif // !SSTA

View File

@ -1,483 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#include "Delay.hh"
#include <cmath> // sqrt
#include "StaConfig.hh"
#include "Error.hh"
#include "StringUtil.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "StaState.hh"
#include "Variables.hh"
// SSTA compilation.
#if (SSTA == 1)
namespace sta {
inline float
square(float x)
{
return x * x;
}
static Delay delay_init_values[MinMax::index_count];
void
initDelayConstants()
{
delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue();
delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue();
}
const Delay &
delayInitValue(const MinMax *min_max)
{
return delay_init_values[min_max->index()];
}
Delay::Delay() :
mean_(0.0),
sigma2_(0.0)
{
}
Delay::Delay(const Delay &delay) :
mean_(delay.mean_),
sigma2_(delay.sigma2_)
{
}
Delay::Delay(const DelayDbl &delay) :
mean_(delay.mean_),
sigma2_(delay.sigma2_)
{
}
Delay::Delay(float mean) :
mean_(mean),
sigma2_(0.0)
{
}
Delay::Delay(float mean,
float sigma2) :
mean_(mean),
sigma2_(sigma2)
{
}
float
Delay::sigma() const
{
if (sigma2_ < 0.0)
// Sigma is negative for crpr to offset sigmas in the common
// clock path.
return -sqrt(-sigma2_);
else
return sqrt(sigma2_);
}
float
Delay::sigma2() const
{
return sigma2_;
}
void
Delay::operator=(const Delay &delay)
{
mean_ = delay.mean_;
sigma2_ = delay.sigma2_;
}
void
Delay::operator=(float delay)
{
mean_ = delay;
sigma2_ = 0.0;
}
void
Delay::operator+=(const Delay &delay)
{
mean_ += delay.mean_;
sigma2_ += delay.sigma2_;
}
void
Delay::operator+=(float delay)
{
mean_ += delay;
}
Delay
Delay::operator+(const Delay &delay) const
{
return Delay(mean_ + delay.mean_,
sigma2_ + delay.sigma2_);
}
Delay
Delay::operator+(float delay) const
{
return Delay(mean_ + delay, sigma2_);
}
Delay
Delay::operator-(const Delay &delay) const
{
return Delay(mean_ - delay.mean_,
sigma2_ + delay.sigma2_);
}
Delay
Delay::operator-(float delay) const
{
return Delay(mean_ - delay, sigma2_);
}
Delay
Delay::operator-() const
{
return Delay(-mean_, sigma2_);
}
void
Delay::operator-=(float delay)
{
mean_ -= delay;
}
void
Delay::operator-=(const Delay &delay)
{
mean_ -= delay.mean_;
sigma2_ += delay.sigma2_;
}
bool
Delay::operator==(const Delay &delay) const
{
return delayEqual(*this, delay);
}
////////////////////////////////////////////////////////////////
DelayDbl::DelayDbl() :
mean_(0.0),
sigma2_(0.0)
{
}
void
DelayDbl::operator=(float delay)
{
mean_ = delay;
sigma2_ = 0.0;
}
void
DelayDbl::operator+=(const Delay &delay)
{
mean_ += delay.mean_;
sigma2_ += delay.sigma2_;
}
void
DelayDbl::operator-=(const Delay &delay)
{
mean_ -= delay.mean_;
sigma2_ += delay.sigma2_;
}
////////////////////////////////////////////////////////////////
Delay
makeDelay(float delay,
float sigma,
float)
{
return Delay(delay, square(sigma));
}
Delay
makeDelay2(float delay,
float sigma2,
float )
{
return Delay(delay, sigma2);
}
float
delayAsFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta)
{
if (sta->variables()->pocvEnabled()) {
if (early_late == EarlyLate::early())
return delay.mean() - delay.sigma() * sta->sigmaFactor();
else if (early_late == EarlyLate::late())
return delay.mean() + delay.sigma() * sta->sigmaFactor();
else
sta->report()->critical(1020, "unknown early/late value.");
}
return delay.mean();
}
float
delaySigma2(const Delay &delay,
const EarlyLate *)
{
return delay.sigma2();
}
const char *
delayAsString(const Delay &delay,
const StaState *sta)
{
return delayAsString(delay, sta, sta->units()->timeUnit()->digits());
}
const char *
delayAsString(const Delay &delay,
const StaState *sta,
int digits)
{
const Unit *unit = sta->units()->timeUnit();
if (sta->variables()->pocvEnabled()) {
float sigma = delay.sigma();
return stringPrintTmp("%s[%s]",
unit->asString(delay.mean(), digits),
unit->asString(sigma, digits));
}
else
return unit->asString(delay.mean(), digits);
}
const char *
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta,
int digits)
{
float mean_sigma = delayAsFloat(delay, early_late, sta);
return sta->units()->timeUnit()->asString(mean_sigma, digits);
}
bool
delayIsInitValue(const Delay &delay,
const MinMax *min_max)
{
return fuzzyEqual(delay.mean(), min_max->initValue())
&& delay.sigma2() == 0.0;
}
bool
delayZero(const Delay &delay)
{
return fuzzyZero(delay.mean())
&& fuzzyZero(delay.sigma2());
}
bool
delayInf(const Delay &delay)
{
return fuzzyInf(delay.mean());
}
bool
delayEqual(const Delay &delay1,
const Delay &delay2)
{
return fuzzyEqual(delay1.mean(), delay2.mean())
&& fuzzyEqual(delay1.sigma2(), delay2.sigma2());
}
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
delayLess(const Delay &delay1,
float delay2,
const StaState *sta)
{
return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta),
delay2);
}
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return delayLess(delay1, delay2, sta);
else
return delayGreater(delay1, delay2, sta);
}
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
delayLessEqual(const Delay &delay1,
float delay2,
const StaState *sta)
{
return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta),
delay2);
}
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return delayLessEqual(delay1, delay2, sta);
else
return delayGreaterEqual(delay1, delay2, sta);
}
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
bool
delayGreater(const Delay &delay1,
float delay2,
const StaState *sta)
{
return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta),
delay2);
}
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
bool
delayGreaterEqual(const Delay &delay1,
float delay2,
const StaState *sta)
{
return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta),
delay2);
}
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return delayGreater(delay1, delay2, sta);
else
return delayLess(delay1, delay2, sta);
}
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return delayGreaterEqual(delay1, delay2, sta);
else
return delayLessEqual(delay1, delay2, sta);
}
Delay
delayRemove(const Delay &delay1,
const Delay &delay2)
{
return Delay(delay1.mean() - delay2.mean(),
delay1.sigma2() - delay2.sigma2());
}
float
delayRatio(const Delay &delay1,
const Delay &delay2)
{
return delay1.mean() / delay2.mean();
}
Delay
operator+(float delay1,
const Delay &delay2)
{
return Delay(delay1 + delay2.mean(),
delay2.sigma2());
}
Delay
operator/(float delay1,
const Delay &delay2)
{
return Delay(delay1 / delay2.mean(),
delay2.sigma2());
}
Delay
operator*(const Delay &delay1,
float delay2)
{
return Delay(delay1.mean() * delay2,
delay1.sigma2() * delay2 * delay2);
}
} // namespace
#endif // (SSTA == 1)

View File

@ -1,517 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#include "Delay.hh"
#include <cmath> // sqrt
#include "StaConfig.hh"
#include "Error.hh"
#include "StringUtil.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "StaState.hh"
// SSTA compilation.
#if (SSTA == 2)
namespace sta {
inline float
square(float x)
{
return x * x;
}
static Delay delay_init_values[MinMax::index_count];
void
initDelayConstants()
{
delay_init_values[MinMax::minIndex()] = MinMax::min()->initValue();
delay_init_values[MinMax::maxIndex()] = MinMax::max()->initValue();
}
const Delay &
delayInitValue(const MinMax *min_max)
{
return delay_init_values[min_max->index()];
}
Delay::Delay() :
mean_(0.0),
sigma2_{0.0, 0.0}
{
}
Delay::Delay(const Delay &delay) :
mean_(delay.mean_)
{
sigma2_[EarlyLate::earlyIndex()] = delay.sigma2_[EarlyLate::earlyIndex()];
sigma2_[EarlyLate::lateIndex()] = delay.sigma2_[EarlyLate::lateIndex()];
}
Delay::Delay(const DelayDbl &delay) :
mean_(delay.mean_)
{
sigma2_[EarlyLate::earlyIndex()] = delay.sigma2_[EarlyLate::earlyIndex()];
sigma2_[EarlyLate::lateIndex()] = delay.sigma2_[EarlyLate::lateIndex()];
}
Delay::Delay(float mean) :
mean_(mean),
sigma2_{0.0, 0.0}
{
}
Delay::Delay(float mean,
float sigma2_early,
float sigma2_late) :
mean_(mean),
sigma2_{sigma2_early, sigma2_late}
{
}
float
Delay::sigma(const EarlyLate *early_late) const
{
float sigma = sigma2_[early_late->index()];
if (sigma < 0.0)
// Sigma is negative for crpr to offset sigmas in the common
// clock path.
return -sqrt(-sigma);
else
return sqrt(sigma);
}
float
Delay::sigma2(const EarlyLate *early_late) const
{
return sigma2_[early_late->index()];
}
float
Delay::sigma2Early() const
{
return sigma2_[early_index];
}
float
Delay::sigma2Late() const
{
return sigma2_[late_index];
}
void
Delay::operator=(const Delay &delay)
{
mean_ = delay.mean_;
sigma2_[early_index] = delay.sigma2_[early_index];
sigma2_[late_index] = delay.sigma2_[late_index];
}
void
Delay::operator=(float delay)
{
mean_ = delay;
sigma2_[early_index] = 0.0;
sigma2_[late_index] = 0.0;
}
void
Delay::operator+=(const Delay &delay)
{
mean_ += delay.mean_;
sigma2_[early_index] += delay.sigma2_[early_index];
sigma2_[late_index] += delay.sigma2_[late_index];
}
void
Delay::operator+=(float delay)
{
mean_ += delay;
}
Delay
Delay::operator+(const Delay &delay) const
{
return Delay(mean_ + delay.mean_,
sigma2_[early_index] + delay.sigma2_[early_index],
sigma2_[late_index] + delay.sigma2_[late_index]);
}
Delay
Delay::operator+(float delay) const
{
return Delay(mean_ + delay, sigma2_[early_index], sigma2_[late_index]);
}
Delay
Delay::operator-(const Delay &delay) const
{
return Delay(mean_ - delay.mean_,
sigma2_[early_index] + delay.sigma2_[late_index],
sigma2_[late_index] + delay.sigma2_[early_index]);
}
Delay
Delay::operator-(float delay) const
{
return Delay(mean_ - delay, sigma2_[early_index], sigma2_[late_index]);
}
Delay
Delay::operator-() const
{
return Delay(-mean_, sigma2_[late_index], sigma2_[early_index]);
}
void
Delay::operator-=(float delay)
{
mean_ -= delay;
}
void
Delay::operator-=(const Delay &delay)
{
mean_ -= delay.mean_;
sigma2_[early_index] += delay.sigma2_[early_index];
sigma2_[late_index] += delay.sigma2_[late_index];
}
bool
Delay::operator==(const Delay &delay) const
{
return mean_ == delay.mean_
&& sigma2_[early_index] == delay.sigma2_[late_index]
&& sigma2_[late_index] == delay.sigma2_[early_index];
}
////////////////////////////////////////////////////////////////
DelayDbl::DelayDbl() :
mean_(0.0),
sigma2_{0.0, 0.0}
{
}
void
DelayDbl::operator=(float delay)
{
mean_ = delay;
sigma2_[early_index] = 0.0;
sigma2_[late_index] = 0.0;
}
void
DelayDbl::operator+=(const Delay &delay)
{
mean_ += delay.mean_;
sigma2_[early_index] += delay.sigma2_[early_index];
sigma2_[late_index] += delay.sigma2_[late_index];
}
void
DelayDbl::operator-=(const Delay &delay)
{
mean_ -= delay.mean_;
sigma2_[early_index] += delay.sigma2_[early_index];
sigma2_[late_index] += delay.sigma2_[late_index];
}
////////////////////////////////////////////////////////////////
Delay
makeDelay(float delay,
float sigma_early,
float sigma_late)
{
return Delay(delay, square(sigma_early), square(sigma_late));
}
Delay
makeDelay2(float delay,
float sigma2_early,
float sigma2_late)
{
return Delay(delay, sigma2_early, sigma2_late);
}
bool
delayIsInitValue(const Delay &delay,
const MinMax *min_max)
{
return fuzzyEqual(delay.mean(), min_max->initValue())
&& fuzzyZero(delay.sigma2Early())
&& fuzzyZero(delay.sigma2Late());
}
bool
delayZero(const Delay &delay)
{
return fuzzyZero(delay.mean())
&& fuzzyZero(delay.sigma2Early())
&& fuzzyZero(delay.sigma2Late());
}
bool
delayInf(const Delay &delay)
{
return fuzzyInf(delay.mean());
}
bool
delayEqual(const Delay &delay1,
const Delay &delay2)
{
return fuzzyEqual(delay1.mean(), delay2.mean())
&& fuzzyEqual(delay1.sigma2Early(), delay2.sigma2Early())
&& fuzzyEqual(delay1.sigma2Late(), delay2.sigma2Late());
}
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
delayLess(const Delay &delay1,
float delay2,
const StaState *sta)
{
return fuzzyLess(delayAsFloat(delay1, EarlyLate::early(), sta),
delay2);
}
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return delayLess(delay1, delay2, sta);
else
return delayGreater(delay1, delay2, sta);
}
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta),
delayAsFloat(delay2, EarlyLate::early(), sta));
}
bool
delayLessEqual(const Delay &delay1,
float delay2,
const StaState *sta)
{
return fuzzyLessEqual(delayAsFloat(delay1, EarlyLate::early(), sta),
delay2);
}
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return delayLessEqual(delay1, delay2, sta);
else
return delayGreaterEqual(delay1, delay2, sta);
}
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return fuzzyGreater(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
bool
delayGreater(const Delay &delay1,
float delay2,
const StaState *sta)
{
return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta)
{
return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta),
delayAsFloat(delay2, EarlyLate::late(), sta));
}
bool
delayGreaterEqual(const Delay &delay1,
float delay2,
const StaState *sta)
{
return fuzzyGreaterEqual(delayAsFloat(delay1, EarlyLate::late(), sta),
delay2);
}
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return delayGreater(delay1, delay2, sta);
else
return delayLess(delay1, delay2, sta);
}
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta)
{
if (min_max == MinMax::max())
return delayGreaterEqual(delay1, delay2, sta);
else
return delayLessEqual(delay1, delay2, sta);
}
float
delayAsFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta)
{
if (sta->pocvEnabled()) {
if (early_late == EarlyLate::early())
return delay.mean() - delay.sigma(early_late) * sta->sigmaFactor();
else if (early_late == EarlyLate::late())
return delay.mean() + delay.sigma(early_late) * sta->sigmaFactor();
else
sta->report()->critical(1030, "unknown early/late value.");
}
return delay.mean();
}
float
delaySigma2(const Delay &delay,
const EarlyLate *early_late)
{
return delay.sigma2(early_late);
}
const char *
delayAsString(const Delay &delay,
const StaState *sta)
{
return delayAsString(delay, sta, sta->units()->timeUnit()->digits());
}
const char *
delayAsString(const Delay &delay,
const StaState *sta,
int digits)
{
const Unit *unit = sta->units()->timeUnit();
if (sta->pocvEnabled()) {
float sigma_early = delay.sigma(EarlyLate::early());
float sigma_late = delay.sigma(EarlyLate::late());
return stringPrintTmp("%s[%s:%s]",
unit->asString(delay.mean(), digits),
unit->asString(sigma_early, digits),
unit->asString(sigma_late, digits));
}
else
return unit->asString(delay.mean(), digits);
}
const char *
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta,
int digits)
{
float mean_sigma = delayAsFloat(delay, early_late, sta);
return sta->units()->timeUnit()->asString(mean_sigma, digits);
}
Delay
delayRemove(const Delay &delay1,
const Delay &delay2)
{
return Delay(delay1.mean() - delay2.mean(),
delay1.sigma2Early() - delay2.sigma2Early(),
delay1.sigma2Late() - delay2.sigma2Late());
}
float
delayRatio(const Delay &delay1,
const Delay &delay2)
{
return delay1.mean() / delay2.mean();
}
Delay
operator+(float delay1,
const Delay &delay2)
{
return Delay(delay1 + delay2.mean(),
delay2.sigma2Early(),
delay2.sigma2Late());
}
Delay
operator/(float delay1,
const Delay &delay2)
{
return Delay(delay1 / delay2.mean(),
delay2.sigma2Early(),
delay2.sigma2Late());
}
Delay
operator*(const Delay &delay1,
float delay2)
{
return Delay(delay1.mean() * delay2,
delay1.sigma2Early() * delay2 * delay2,
delay1.sigma2Late() * delay2 * delay2);
}
} // namespace
#endif // (SSTA == 2)

View File

@ -36,6 +36,7 @@
#include "PortDirection.hh"
#include "Network.hh"
#include "FuncExpr.hh"
#include "Variables.hh"
namespace sta {
@ -46,12 +47,10 @@ namespace sta {
////////////////////////////////////////////////////////////////
Graph::Graph(StaState *sta,
int slew_rf_count,
DcalcAPIndex ap_count) :
StaState(sta),
vertices_(nullptr),
edges_(nullptr),
slew_rf_count_(slew_rf_count),
ap_count_(ap_count),
period_check_annotations_(nullptr),
reg_clk_vertices_(makeVertexSet(this))
@ -284,7 +283,7 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin,
if (isIsolatedNet(drvrs, loads)) {
for (auto drvr_pin : drvrs) {
visited_drvrs.insert(drvr_pin);
debugPrint(debug_, "graph", 1, "ignoring isolated driver %s",
debugPrint(debug_, "graph", 1, "ignoring isolated driver {}",
network_->pathName(drvr_pin));
}
return;
@ -578,22 +577,32 @@ Graph::gateEdgeArc(const Pin *in_pin,
////////////////////////////////////////////////////////////////
const Slew &
Slew
Graph::slew(const Vertex *vertex,
const RiseFall *rf,
DcalcAPIndex ap_index)
{
if (slew_rf_count_) {
const Slew *slews = vertex->slews();
size_t slew_index = (slew_rf_count_ == 1)
? ap_index
: ap_index*slew_rf_count_+rf->index();
size_t slew_index = ap_index * RiseFall::index_count + rf->index();
const float *slews_flt = vertex->slewsFloat();
if (variables_->pocvEnabled()) {
const Slew *slews = std::bit_cast<const Slew*>(slews_flt);
return slews[slew_index];
}
else {
static Slew slew(0.0);
return slew;
else
return slews_flt[slew_index];
}
Slew
Graph::slew(const Vertex *vertex,
size_t index)
{
const float *slews_flt = vertex->slewsFloat();
if (variables_->pocvEnabled()) {
const Slew *slews = std::bit_cast<const Slew*>(slews_flt);
return slews[index];
}
else
return slews_flt[index];
}
void
@ -602,18 +611,15 @@ Graph::setSlew(Vertex *vertex,
DcalcAPIndex ap_index,
const Slew &slew)
{
if (slew_rf_count_) {
size_t slew_index = ap_index * RiseFall::index_count + rf->index();
if (variables_->pocvEnabled()) {
Slew *slews = vertex->slews();
if (slews == nullptr) {
int slew_count = slew_rf_count_ * ap_count_;
slews = new Slew[slew_count];
vertex->setSlews(slews);
}
size_t slew_index = (slew_rf_count_ == 1)
? ap_index
: ap_index*slew_rf_count_+rf->index();
slews[slew_index] = slew;
}
else {
float *slews_flt = vertex->slewsFloat();
slews_flt[slew_index] = slew.mean();
}
}
////////////////////////////////////////////////////////////////
@ -670,30 +676,48 @@ Graph::arcDelay(const Edge *edge,
const TimingArc *arc,
DcalcAPIndex ap_index) const
{
ArcDelay *delays = edge->arcDelays();
size_t index = arc->index() * ap_count_ + ap_index;
return delays[index];
if (variables_->pocvEnabled()) {
ArcDelay *delays = std::bit_cast<ArcDelay*>(edge->arcDelays());
return delays[index];
}
else {
const float *delays = edge->arcDelays();
return delays[index];
}
}
void
Graph::setArcDelay(Edge *edge,
const TimingArc *arc,
DcalcAPIndex ap_index,
ArcDelay delay)
const ArcDelay &delay)
{
ArcDelay *arc_delays = edge->arcDelays();
size_t index = arc->index() * ap_count_ + ap_index;
arc_delays[index] = delay;
if (variables_->pocvEnabled()) {
ArcDelay *delays = std::bit_cast<ArcDelay*>(edge->arcDelays());
delays[index] = delay;
}
else {
float *delays = edge->arcDelays();
delays[index] = delay.mean();
}
}
const ArcDelay &
ArcDelay
Graph::wireArcDelay(const Edge *edge,
const RiseFall *rf,
DcalcAPIndex ap_index)
{
ArcDelay *delays = edge->arcDelays();
size_t index = rf->index() * ap_count_ + ap_index;
return delays[index];
if (variables_->pocvEnabled()) {
ArcDelay *delays = std::bit_cast<ArcDelay*>(edge->arcDelays());
return delays[index];
}
else {
const float *delays = edge->arcDelays();
return delays[index];
}
}
void
@ -702,9 +726,15 @@ Graph::setWireArcDelay(Edge *edge,
DcalcAPIndex ap_index,
const ArcDelay &delay)
{
ArcDelay *delays = edge->arcDelays();
size_t index = rf->index() * ap_count_ + ap_index;
delays[index] = delay;
if (variables_->pocvEnabled()) {
ArcDelay *delays = std::bit_cast<ArcDelay*>(edge->arcDelays());
delays[index] = delay;
}
else {
float *delays = edge->arcDelays();
delays[index] = delay.mean();
}
}
////////////////////////////////////////////////////////////////
@ -788,16 +818,20 @@ void
Graph::initSlews(Vertex *vertex)
{
size_t slew_count = slewCount();
Slew *slews = new Slew[slew_count];
vertex->setSlews(slews);
for (size_t i = 0; i < slew_count; i++)
slews[i] = 0.0;
if (variables_->pocvEnabled()) {
float *slews = std::bit_cast<float*>(new Slew[slew_count]{});
vertex->setSlews(slews);
}
else {
float *slews = new float[slew_count]{};
vertex->setSlews(slews);
}
}
size_t
Graph::slewCount()
{
return slew_rf_count_ * ap_count_;
return RiseFall::index_count * ap_count_;
}
void
@ -805,10 +839,14 @@ Graph::initArcDelays(Edge *edge)
{
size_t arc_count = edge->timingArcSet()->arcCount();
size_t delay_count = arc_count * ap_count_;
ArcDelay *arc_delays = new ArcDelay[delay_count];
edge->setArcDelays(arc_delays);
for (size_t i = 0; i < delay_count; i++)
arc_delays[i] = 0.0;
if (variables_->pocvEnabled()) {
float *delays = std::bit_cast<float*>(new ArcDelay[delay_count]{});
edge->setArcDelays(delays);
}
else {
float *delays = new float[delay_count]{};
edge->setArcDelays(delays);
}
}
////////////////////////////////////////////////////////////////
@ -1040,7 +1078,7 @@ Vertex::setVisited2(bool visited)
}
void
Vertex::setSlews(Slew *slews)
Vertex::setSlews(float *slews)
{
delete [] slews_;
slews_ = slews;
@ -1251,10 +1289,10 @@ Edge::setTimingArcSet(TimingArcSet *set)
}
void
Edge::setArcDelays(ArcDelay *arc_delays)
Edge::setArcDelays(float *delays)
{
delete [] arc_delays_;
arc_delays_ = arc_delays;
arc_delays_ = delays;
}
bool

View File

@ -132,21 +132,24 @@ bool is_bidirect_driver() { return self->isBidirectDriver(); }
int level() { return Sta::sta()->vertexLevel(self); }
int tag_group_index() { return self->tagGroupIndex(); }
Slew
float
slew(const RiseFallBoth *rf,
const MinMax *min_max)
{
Sta *sta = Sta::sta();
return sta->slew(self, rf, sta->scenes(), min_max);
return delayAsFloat(sta->slew(self, rf, sta->scenes(), min_max), min_max, sta);
}
Slew
slew_scenes(const RiseFallBoth *rf,
const SceneSeq scenes,
const MinMax *min_max)
std::string
slew_scenes_string(const RiseFallBoth *rf,
const SceneSeq scenes,
const MinMax *min_max,
bool report_variance,
int digits)
{
Sta *sta = Sta::sta();
return sta->slew(self, rf, scenes, min_max);
Slew slew = sta->slew(self, rf, scenes, min_max);
return delayAsString(slew, min_max, report_variance, digits, sta);
}
VertexOutEdgeIterator *
@ -223,22 +226,29 @@ arc_delays(TimingArc *arc)
{
Sta *sta = Sta::sta();
FloatSeq delays;
DcalcAPIndex ap_count = sta->dcalcAnalysisPtCount();
for (DcalcAPIndex ap_index = 0; ap_index < ap_count; ap_index++)
delays.push_back(delayAsFloat(sta->arcDelay(self, arc, ap_index)));
for (Scene *scene : sta->scenes()) {
for (const MinMax *min_max : MinMax::range()) {
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
delays.push_back(delayAsFloat(sta->arcDelay(self, arc, ap_index), min_max, sta));
}
}
return delays;
}
StringSeq
arc_delay_strings(TimingArc *arc,
bool report_variance,
int digits)
{
Sta *sta = Sta::sta();
StringSeq delays;
DcalcAPIndex ap_count = sta->dcalcAnalysisPtCount();
for (DcalcAPIndex ap_index = 0; ap_index < ap_count; ap_index++)
delays.push_back(delayAsString(sta->arcDelay(self, arc, ap_index),
sta, digits));
for (Scene *scene : sta->scenes()) {
for (const MinMax *min_max : MinMax::range()) {
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
delays.push_back(delayAsString(sta->arcDelay(self, arc, ap_index),
min_max, report_variance, digits, sta));
}
}
return delays;
}
@ -255,8 +265,9 @@ arc_delay(TimingArc *arc,
const Scene *scene,
const MinMax *min_max)
{
Sta *sta = Sta::sta();
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
return delayAsFloat(Sta::sta()->arcDelay(self, arc, ap_index));
return delayAsFloat(Sta::sta()->arcDelay(self, arc, ap_index), min_max, sta);
}
std::string
@ -281,7 +292,7 @@ mode_value()
return self->timingArcSet()->modeValue().c_str();
}
const char *
std::string
latch_d_to_q_en()
{
if (self->role() == TimingRole::latchDtoQ()) {
@ -297,9 +308,7 @@ latch_d_to_q_en()
const RiseFall *enable_rf;
lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf);
if (enable_port)
return stringPrintTmp("%s %s",
enable_port->name(),
enable_rf->shortName());
return sta::format("{} {}", enable_port->name(), enable_rf->shortName());
}
return "";
}

View File

@ -26,51 +26,64 @@
namespace eval sta {
define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]}
define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]\
[-digits digits] [-report_variance]}
proc report_edges { args } {
parse_key_args "report_edges" args keys {-from -to} flags {}
global sta_report_default_digits
parse_key_args "report_edges" args keys {-from -to -digits} flags {-report_variance}
check_argc_eq0 "report_edges" $args
if [info exists keys(-digits)] {
set digits $keys(-digits)
check_positive_integer "-digits" $digits
} else {
set digits $sta_report_default_digits
}
set report_variance [info exists flags(-report_variance)]
if { [info exists keys(-from)] && [info exists keys(-to)] } {
set from_pin [get_port_pin_error "from_pin" $keys(-from)]
set to_pin [get_port_pin_error "to_pin" $keys(-to)]
foreach from_vertex [$from_pin vertices] {
foreach to_vertex [$to_pin vertices] {
report_edges_between_ $from_vertex $to_vertex
report_edges_between_ $from_vertex $to_vertex $digits $report_variance
}
}
} elseif [info exists keys(-from)] {
set from_pin [get_port_pin_error "from_pin" $keys(-from)]
foreach from_vertex [$from_pin vertices] {
report_edges_ $from_vertex out_edge_iterator \
vertex_port_name vertex_path_name
vertex_port_name vertex_path_name $digits $report_variance
}
} elseif [info exists keys(-to)] {
set to_pin [get_port_pin_error "to_pin" $keys(-to)]
foreach to_vertex [$to_pin vertices] {
report_edges_ $to_vertex in_edge_iterator \
vertex_path_name vertex_port_name
vertex_path_name vertex_port_name $digits $report_variance
}
}
}
proc report_edges_between_ { from_vertex to_vertex } {
proc report_edges_between_ { from_vertex to_vertex digits report_variance } {
set iter [$from_vertex out_edge_iterator]
while {[$iter has_next]} {
set edge [$iter next]
if { [$edge to] == $to_vertex } {
if { [$edge role] == "wire" } {
report_edge_ $edge vertex_path_name vertex_path_name
report_edge_ $edge vertex_path_name vertex_path_name $digits $report_variance
} else {
report_edge_ $edge vertex_port_name vertex_port_name
report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance
}
}
}
$iter finish
}
proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } {
proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc \
digits report_variance } {
# First report edges internal to the device.
set device_header 0
set iter [$vertex $iter_proc]
@ -84,7 +97,7 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } {
}
set device_header 1
}
report_edge_ $edge vertex_port_name vertex_port_name
report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance
}
}
$iter finish
@ -94,15 +107,14 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } {
while {[$iter has_next]} {
set edge [$iter next]
if { [$edge role] == "wire" } {
report_edge_ $edge $wire_from_name_proc $wire_to_name_proc
report_edge_ $edge $wire_from_name_proc $wire_to_name_proc $digits $report_variance
}
}
$iter finish
}
proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } {
global sta_report_default_digits
proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc \
digits report_variance } {
set latch_enable [$edge latch_d_to_q_en]
if { $latch_enable != "" } {
set latch_enable " enable $latch_enable"
@ -125,7 +137,7 @@ proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } {
}
foreach arc [$edge timing_arcs] {
set delays [$edge arc_delay_strings $arc $sta_report_default_digits]
set delays [$edge arc_delay_strings $arc $report_variance $digits]
set delays_fmt [format_delays $delays]
set disable_reason ""
if { [timing_arc_disabled $edge $arc] } {
@ -135,18 +147,6 @@ proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } {
}
}
# Separate list elements with colons.
proc format_times { values digits } {
set result ""
foreach value $values {
if { $result != "" } {
append result ":"
}
append result [format_time $value $digits]
}
return $result
}
# Separate delay list elements with colons.
proc format_delays { values } {
set result ""

View File

@ -1,5 +1,7 @@
#include <gtest/gtest.h>
#include "DelayFloat.hh"
#include "Delay.hh"
#include "DelayScalar.hh"
#include "StaState.hh"
#include "MinMax.hh"
#include "Graph.hh"
#include "Transition.hh"
@ -9,38 +11,55 @@
namespace sta {
// Minimal StaState subclass that provides a DelayOps for unit tests.
class TestStaState : public StaState
{
public:
TestStaState() { delay_ops_ = new DelayOpsScalar(); }
~TestStaState() override { delete delay_ops_; delay_ops_ = nullptr; }
};
class DelayFloatTest : public ::testing::Test {
protected:
void SetUp() override {
initDelayConstants();
sta_state_ = new TestStaState();
}
void TearDown() override {
delete sta_state_;
sta_state_ = nullptr;
}
// Convenience accessor for delay comparison functions.
const StaState *sta() const { return sta_state_; }
TestStaState *sta_state_;
};
TEST_F(DelayFloatTest, DelayZero) {
EXPECT_TRUE(delayZero(0.0f));
EXPECT_TRUE(delayZero(delay_zero));
EXPECT_FALSE(delayZero(1.0f));
EXPECT_FALSE(delayZero(-1.0f));
EXPECT_TRUE(delayZero(Delay(0.0f), sta()));
EXPECT_TRUE(delayZero(delay_zero, sta()));
EXPECT_FALSE(delayZero(Delay(1.0f), sta()));
EXPECT_FALSE(delayZero(Delay(-1.0f), sta()));
}
TEST_F(DelayFloatTest, DelayEqual) {
EXPECT_TRUE(delayEqual(1.0f, 1.0f));
EXPECT_TRUE(delayEqual(0.0f, 0.0f));
EXPECT_FALSE(delayEqual(1.0f, 2.0f));
EXPECT_TRUE(delayEqual(Delay(1.0f), Delay(1.0f), sta()));
EXPECT_TRUE(delayEqual(Delay(0.0f), Delay(0.0f), sta()));
EXPECT_FALSE(delayEqual(Delay(1.0f), Delay(2.0f), sta()));
}
TEST_F(DelayFloatTest, DelayInf) {
// delayInf checks against STA's INF constant, not IEEE infinity
EXPECT_TRUE(delayInf(INF));
EXPECT_TRUE(delayInf(-INF));
EXPECT_FALSE(delayInf(0.0f));
EXPECT_FALSE(delayInf(1e10f));
EXPECT_TRUE(delayInf(Delay(INF), sta()));
EXPECT_TRUE(delayInf(Delay(-INF), sta()));
EXPECT_FALSE(delayInf(Delay(0.0f), sta()));
EXPECT_FALSE(delayInf(Delay(1e10f), sta()));
}
TEST_F(DelayFloatTest, DelayLess) {
EXPECT_TRUE(delayLess(1.0f, 2.0f, nullptr));
EXPECT_FALSE(delayLess(2.0f, 1.0f, nullptr));
EXPECT_FALSE(delayLess(1.0f, 1.0f, nullptr));
EXPECT_TRUE(delayLess(Delay(1.0f), Delay(2.0f), sta()));
EXPECT_FALSE(delayLess(Delay(2.0f), Delay(1.0f), sta()));
EXPECT_FALSE(delayLess(Delay(1.0f), Delay(1.0f), sta()));
}
TEST_F(DelayFloatTest, DelayRemove) {
@ -50,11 +69,6 @@ TEST_F(DelayFloatTest, DelayRemove) {
EXPECT_FLOAT_EQ(result, 2.0f);
}
TEST_F(DelayFloatTest, DelayRatio) {
EXPECT_FLOAT_EQ(delayRatio(6.0f, 3.0f), 2.0f);
EXPECT_FLOAT_EQ(delayRatio(0.0f, 1.0f), 0.0f);
}
TEST_F(DelayFloatTest, DelayInitValueMin) {
const Delay &init = delayInitValue(MinMax::min());
// Min init value should be a large positive number
@ -68,8 +82,8 @@ TEST_F(DelayFloatTest, DelayInitValueMax) {
}
TEST_F(DelayFloatTest, MakeDelay) {
Delay d = makeDelay(1.5f, 0.0f, 0.0f);
EXPECT_FLOAT_EQ(d, 1.5f);
Delay d = makeDelay(1.5f, 0.0f);
EXPECT_FLOAT_EQ(delayAsFloat(d), 1.5f);
}
TEST_F(DelayFloatTest, DelayAsFloat) {
@ -79,33 +93,33 @@ TEST_F(DelayFloatTest, DelayAsFloat) {
// Additional delay tests for improved coverage
TEST_F(DelayFloatTest, DelayGreater) {
EXPECT_TRUE(delayGreater(2.0f, 1.0f, nullptr));
EXPECT_FALSE(delayGreater(1.0f, 2.0f, nullptr));
EXPECT_FALSE(delayGreater(1.0f, 1.0f, nullptr));
EXPECT_TRUE(delayGreater(Delay(2.0f), Delay(1.0f), sta()));
EXPECT_FALSE(delayGreater(Delay(1.0f), Delay(2.0f), sta()));
EXPECT_FALSE(delayGreater(Delay(1.0f), Delay(1.0f), sta()));
}
TEST_F(DelayFloatTest, DelayLessEqual) {
EXPECT_TRUE(delayLessEqual(1.0f, 2.0f, nullptr));
EXPECT_TRUE(delayLessEqual(1.0f, 1.0f, nullptr));
EXPECT_FALSE(delayLessEqual(2.0f, 1.0f, nullptr));
EXPECT_TRUE(delayLessEqual(Delay(1.0f), Delay(2.0f), sta()));
EXPECT_TRUE(delayLessEqual(Delay(1.0f), Delay(1.0f), sta()));
EXPECT_FALSE(delayLessEqual(Delay(2.0f), Delay(1.0f), sta()));
}
TEST_F(DelayFloatTest, DelayGreaterEqual) {
EXPECT_TRUE(delayGreaterEqual(2.0f, 1.0f, nullptr));
EXPECT_TRUE(delayGreaterEqual(1.0f, 1.0f, nullptr));
EXPECT_FALSE(delayGreaterEqual(1.0f, 2.0f, nullptr));
EXPECT_TRUE(delayGreaterEqual(Delay(2.0f), Delay(1.0f), sta()));
EXPECT_TRUE(delayGreaterEqual(Delay(1.0f), Delay(1.0f), sta()));
EXPECT_FALSE(delayGreaterEqual(Delay(1.0f), Delay(2.0f), sta()));
}
TEST_F(DelayFloatTest, MakeDelayWithSigma) {
// In float mode, sigma is ignored
Delay d = makeDelay(2.5f, 0.1f, 0.2f);
EXPECT_FLOAT_EQ(d, 2.5f);
// makeDelay(mean, std_dev) - sigma stored in Delay class
Delay d = makeDelay(2.5f, 0.1f);
EXPECT_FLOAT_EQ(delayAsFloat(d), 2.5f);
}
TEST_F(DelayFloatTest, DelayNegative) {
Delay d = -5.0f;
EXPECT_FLOAT_EQ(delayAsFloat(d), -5.0f);
EXPECT_FALSE(delayZero(d));
EXPECT_FALSE(delayZero(d, sta()));
}
////////////////////////////////////////////////////////////////
@ -274,11 +288,12 @@ TEST(VertexStandaloneTest, Paths)
EXPECT_EQ(v.paths(), nullptr);
}
// Test Vertex slews
TEST(VertexStandaloneTest, Slews)
// Test Vertex pin default
TEST(VertexStandaloneTest, PinDefault)
{
Vertex v;
EXPECT_EQ(v.slews(), nullptr);
// slews() is protected; verify public pin() accessor instead
EXPECT_EQ(v.pin(), nullptr);
}
////////////////////////////////////////////////////////////////
@ -360,10 +375,10 @@ TEST(EdgeStandaloneTest, RemoveDelayAnnotated)
TEST(EdgeStandaloneTest, SetArcDelays)
{
Edge e;
// Set and clear arc delays
ArcDelay *delays = new ArcDelay[4];
// Set and clear arc delays (setArcDelays takes float*)
float *delays = new float[4];
for (int i = 0; i < 4; i++)
delays[i] = 0.0;
delays[i] = 0.0f;
e.setArcDelays(delays);
EXPECT_NE(e.arcDelays(), nullptr);
e.setArcDelays(nullptr);
@ -385,14 +400,14 @@ TEST_F(DelayFloatTest, DelayLessEqualMinMax)
{
// 4-arg delayLessEqual with MinMax
// With max: same as fuzzyLessEqual
EXPECT_TRUE(delayLessEqual(1.0f, 2.0f, MinMax::max(), nullptr));
EXPECT_TRUE(delayLessEqual(1.0f, 1.0f, MinMax::max(), nullptr));
EXPECT_FALSE(delayLessEqual(2.0f, 1.0f, MinMax::max(), nullptr));
EXPECT_TRUE(delayLessEqual(Delay(1.0f), Delay(2.0f), MinMax::max(), sta()));
EXPECT_TRUE(delayLessEqual(Delay(1.0f), Delay(1.0f), MinMax::max(), sta()));
EXPECT_FALSE(delayLessEqual(Delay(2.0f), Delay(1.0f), MinMax::max(), sta()));
// With min: same as fuzzyGreaterEqual (reversed)
EXPECT_TRUE(delayLessEqual(2.0f, 1.0f, MinMax::min(), nullptr));
EXPECT_TRUE(delayLessEqual(1.0f, 1.0f, MinMax::min(), nullptr));
EXPECT_FALSE(delayLessEqual(1.0f, 2.0f, MinMax::min(), nullptr));
EXPECT_TRUE(delayLessEqual(Delay(2.0f), Delay(1.0f), MinMax::min(), sta()));
EXPECT_TRUE(delayLessEqual(Delay(1.0f), Delay(1.0f), MinMax::min(), sta()));
EXPECT_FALSE(delayLessEqual(Delay(1.0f), Delay(2.0f), MinMax::min(), sta()));
}
////////////////////////////////////////////////////////////////
@ -413,7 +428,8 @@ TEST(EdgeStandaloneTest, DefaultState)
TEST(VertexStandaloneTest, SlewsDefault)
{
Vertex v;
EXPECT_EQ(v.slews(), nullptr);
// slews() is protected; verify public paths() accessor instead
EXPECT_EQ(v.paths(), nullptr);
}
// Test Edge::arcDelayAnnotateBit - static method
@ -440,22 +456,14 @@ TEST(EdgeStandaloneTest, EdgeInitViaTimingArcSet)
EXPECT_EQ(e.timingArcSet(), nullptr);
}
// Test Vertex setSlews
// Covers: Vertex::setSlews
// Test Vertex initial state via public accessors
// Covers: Vertex default state
TEST(VertexStandaloneTest, SetSlews)
{
Vertex v;
EXPECT_EQ(v.slews(), nullptr);
// Allocate some slews
Slew *slews = new Slew[4];
for (int i = 0; i < 4; i++)
slews[i] = static_cast<float>(i) * 1e-9f;
// setSlews is protected, but we test via the public slews() accessor
// We can't directly call setSlews, but we verify initial state
EXPECT_EQ(v.slews(), nullptr);
delete[] slews;
// slews() and setSlews() are protected; verify public accessors instead
EXPECT_EQ(v.pin(), nullptr);
EXPECT_EQ(v.paths(), nullptr);
}
// Test Vertex setPaths
@ -541,14 +549,12 @@ TEST(EdgeStandaloneTest, SetTimingArcSetNull)
EXPECT_EQ(e.timingArcSet(), nullptr);
}
// Test Vertex setSlews indirectly - slews_ is protected
// Covers: Vertex::setSlews path
// Test Vertex public accessors for initial state
// Covers: Vertex::paths, Vertex::setPaths
TEST(VertexStandaloneTest, VertexSlewsProtected)
{
Vertex v;
// Initially slews_ is nullptr
EXPECT_EQ(v.slews(), nullptr);
// setPaths is public - at least verify paths
// slews() is protected; verify public setPaths/paths instead
v.setPaths(nullptr);
EXPECT_EQ(v.paths(), nullptr);
}
@ -673,7 +679,8 @@ TEST(EdgeStandaloneTest, ArcDelaysSetAndAccess)
{
Edge e;
EXPECT_EQ(e.arcDelays(), nullptr);
ArcDelay *delays = new ArcDelay[8];
// setArcDelays takes float*
float *delays = new float[8];
for (int i = 0; i < 8; i++)
delays[i] = static_cast<float>(i) * 1e-12f;
e.setArcDelays(delays);
@ -773,14 +780,14 @@ TEST(EdgeStandaloneTest, MultipleFlagCombinations)
TEST_F(DelayFloatTest, DelayLessEqualMinMaxVariant)
{
// With max: standard less-equal
EXPECT_TRUE(delayLessEqual(1.0f, 2.0f, MinMax::max(), nullptr));
EXPECT_TRUE(delayLessEqual(2.0f, 2.0f, MinMax::max(), nullptr));
EXPECT_FALSE(delayLessEqual(3.0f, 2.0f, MinMax::max(), nullptr));
EXPECT_TRUE(delayLessEqual(Delay(1.0f), Delay(2.0f), MinMax::max(), sta()));
EXPECT_TRUE(delayLessEqual(Delay(2.0f), Delay(2.0f), MinMax::max(), sta()));
EXPECT_FALSE(delayLessEqual(Delay(3.0f), Delay(2.0f), MinMax::max(), sta()));
// With min: reversed (greater-equal)
EXPECT_TRUE(delayLessEqual(3.0f, 2.0f, MinMax::min(), nullptr));
EXPECT_TRUE(delayLessEqual(2.0f, 2.0f, MinMax::min(), nullptr));
EXPECT_FALSE(delayLessEqual(1.0f, 2.0f, MinMax::min(), nullptr));
EXPECT_TRUE(delayLessEqual(Delay(3.0f), Delay(2.0f), MinMax::min(), sta()));
EXPECT_TRUE(delayLessEqual(Delay(2.0f), Delay(2.0f), MinMax::min(), sta()));
EXPECT_FALSE(delayLessEqual(Delay(1.0f), Delay(2.0f), MinMax::min(), sta()));
}
////////////////////////////////////////////////////////////////

View File

@ -119,15 +119,15 @@ public:
ArcDcalcResult(size_t load_count);
void setLoadCount(size_t load_count);
ArcDelay &gateDelay() { return gate_delay_; }
void setGateDelay(ArcDelay gate_delay);
void setGateDelay(const ArcDelay &gate_delay);
Slew &drvrSlew() { return drvr_slew_; }
void setDrvrSlew(Slew drvr_slew);
ArcDelay wireDelay(size_t load_idx) const;
void setDrvrSlew(const Slew &drvr_slew);
const ArcDelay &wireDelay(size_t load_idx) const;
void setWireDelay(size_t load_idx,
ArcDelay wire_delay);
Slew loadSlew(size_t load_idx) const;
const ArcDelay &wire_delay);
const Slew &loadSlew(size_t load_idx) const;
void setLoadSlew(size_t load_idx,
Slew load_slew);
const Slew &load_slew);
protected:
ArcDelay gate_delay_;

View File

@ -25,6 +25,7 @@
#pragma once
#include <map>
#include <string>
#include "MinMax.hh"
#include "RiseFallMinMax.hh"
@ -207,7 +208,7 @@ public:
~ClockEdge();
const RiseFall *transition() const { return rf_; }
float time() const { return time_; }
const char *name() const { return name_; }
const std::string &name() const { return name_; }
int index() const { return index_; }
ClockEdge *opposite() const;
// Pulse width if this is the leading edge of the pulse.
@ -221,7 +222,7 @@ private:
Clock *clock_;
const RiseFall *rf_;
const char *name_;
std::string name_;
float time_;
int index_;
};

View File

@ -24,6 +24,8 @@
#pragma once
#include <string>
#include "SdcCmdComment.hh"
#include "SdcClass.hh"
@ -32,7 +34,7 @@ namespace sta {
class ClockGroups : public SdcCmdComment
{
public:
ClockGroups(const char *name,
ClockGroups(const std::string &name,
bool logically_exclusive,
bool physically_exclusive,
bool asynchronous,
@ -40,7 +42,7 @@ public:
const char *comment);
~ClockGroups();
void makeClockGroup(ClockSet *clks);
const char *name() const { return name_; }
const std::string &name() const { return name_; }
ClockGroupSet *groups() { return &groups_; }
bool logicallyExclusive() const { return logically_exclusive_; }
bool physicallyExclusive() const { return physically_exclusive_; }
@ -49,7 +51,7 @@ public:
void removeClock(Clock *clk);
private:
const char *name_;
std::string name_;
bool logically_exclusive_;
bool physically_exclusive_;
bool asynchronous_;

View File

@ -25,10 +25,12 @@
#pragma once
#include <string>
#include <cstdarg>
#include <string_view>
#include <map>
#include <mutex>
#include "Format.hh"
#include "Report.hh"
#include "StringUtil.hh"
namespace sta {
@ -48,10 +50,16 @@ public:
bool check(const char *what,
int level) const;
int statsLevel() const { return stats_level_; }
void reportLine(const char *what,
const char *fmt,
...)
__attribute__((format (printf, 3, 4)));
template <typename... Args>
void report(const char *what,
std::string_view fmt,
Args &&...args)
{
std::string msg = sta::format("{}: {}", what,
sta::formatRuntime(fmt, std::forward<Args>(args)...));
std::unique_lock<std::mutex> lock(buffer_lock_);
report_->reportLine(msg);
}
protected:
Report *report_;
@ -63,9 +71,9 @@ protected:
// Inlining a varargs function would eval the args, which can
// be expensive, so use a macro.
#define debugPrint(debug, what, level, ...) \
#define debugPrint(debug, what, level, fmt, ...) \
if (debug->check(what, level)) { \
debug->reportLine(what __VA_OPT__(,) __VA_ARGS__); \
debug->report(what, fmt __VA_OPT__(,) __VA_ARGS__); \
}
} // namespace

View File

@ -24,27 +24,336 @@
#pragma once
#include "StaConfig.hh"
#include <array>
#include <cstddef>
// IWYU pragma: begin_exports
#if (SSTA == 1)
// Delays are Normal PDFs.
#include "DelayNormal1.hh"
#elif (SSTA == 2)
// Delays are Normal PDFs with early/late sigma.
#include "DelayNormal2.hh"
#else
// Delays are floats.
#include "DelayFloat.hh"
#endif
// IWYU pragma: end_exports
#include "StaConfig.hh"
#include "MinMax.hh"
namespace sta {
class StaState;
class Delay
{
public:
Delay();
Delay(float mean);
Delay(float mean,
// std_dev^2
float std_dev2);
Delay(float mean,
float mean_shift,
// std_dev^2
float std_dev2,
float skewness);
void setValues(float mean,
float mean_shift,
float std_dev2,
float skewnes);
float mean() const { return values_[0]; }
void setMean(float mean);
float meanShift() const { return values_[1]; }
void setMeanShift(float mean_shift);
float stdDev() const;
// std_dev ^ 2
float stdDev2() const { return values_[2]; }
void setStdDev(float std_dev);
float skewness() const { return values_[3]; }
void setSkewness(float skewness);
void operator=(float delay);
// This allows applications that do not support statistical timing
// to treat Delays as floats without explicitly converting with
// delayAsFloat.
operator float() const { return mean(); }
private:
std::array<float, 4> values_;
};
// Delay with doubles for accumulating Delays.
// Only a subset of operations are required for DelayDbl.
class DelayDbl
{
public:
DelayDbl();
DelayDbl(double value);
double mean() const { return values_[0]; }
void setMean(double mean);
double meanShift() const { return values_[1]; }
// std_dev ^ 2
double stdDev2() const { return values_[2]; }
double stdDev() const;
double skewness() const { return values_[3]; }
void setValues(double mean,
double mean_shift,
double std_dev2,
double skewnes);
void operator=(double delay);
private:
std::array<double, 4> values_;
};
using ArcDelay = Delay;
using Slew = Delay;
using Arrival = Delay;
using Required = Delay;
using Slack = Delay;
const Delay delay_zero(0.0);
class DelayOps
{
public:
virtual ~DelayOps() {}
virtual float stdDev2(const Delay &delay,
const EarlyLate *early_late) const = 0;
virtual float asFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta) const = 0;
virtual double asFloat(const DelayDbl &delay,
const EarlyLate *early_late,
const StaState *sta) const = 0;
virtual bool isZero(const Delay &delay) const = 0;
virtual bool isInf(const Delay &delay) const = 0;
virtual bool equal(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const = 0;
virtual bool less(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const = 0;
virtual bool less(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *sta) const = 0;
virtual bool lessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const = 0;
virtual bool greater(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const = 0;
virtual bool greaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const = 0;
virtual Delay sum(const Delay &delay1,
const Delay &delay2) const = 0;
virtual Delay sum(const Delay &delay1,
float delay2) const = 0;
virtual Delay diff(const Delay &delay1,
const Delay &delay2) const = 0;
virtual Delay diff(const Delay &delay1,
float delay2) const = 0;
virtual Delay diff(float delay1,
const Delay &delay2) const = 0;
virtual void incr(Delay &delay1,
const Delay &delay2) const = 0;
virtual void incr(DelayDbl &delay1,
const Delay &delay2) const = 0;
virtual void decr(Delay &delay1,
const Delay &delay2) const = 0;
virtual void decr(DelayDbl &delay1,
const Delay &delay2) const = 0;
virtual Delay product(const Delay &delay1,
float delay2) const = 0;
virtual Delay div(float delay1,
const Delay &delay2) const = 0;
virtual std::string asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const = 0;
};
void
initDelayConstants();
inline float
square(float x)
{
return x * x;
}
inline double
square(double x)
{
return x * x;
}
inline float
cube(float x)
{
return x * x * x;
}
inline double
cube(double x)
{
return x * x * x;
}
Delay
makeDelay(float mean,
float mean_shift,
float std_dev,
float skewness);
Delay
makeDelay(float mean,
float std_dev);
Delay
makeDelay2(float mean,
float std_dev);
void
delaySetMean(Delay &delay,
float mean);
// early_late == late
std::string
delayAsString(const Delay &delay,
const StaState *sta);
// early_late == late
std::string
delayAsString(const Delay &delay,
int digits,
const StaState *sta);
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta);
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
int digits,
const StaState *sta);
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
bool report_variance,
int digits,
const StaState *sta);
float
delayAsFloat(const Delay &delay);
float
delayAsFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta);
float
delayAsFloat(const DelayDbl &delay,
const EarlyLate *early_late,
const StaState *sta);
Delay
delayDblAsDelay(DelayDbl &delay);
Delay
delaySum(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
Delay
delaySum(const Delay &delay1,
float delay2,
const StaState *sta);
Delay
delayDiff(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
Delay
delayDiff(const Delay &delay1,
float delay2,
const StaState *sta);
Delay
delayDiff(float delay1,
const Delay &delay2,
const StaState *sta);
void
delayIncr(Delay &delay1,
const Delay &delay2,
const StaState *sta);
void
delayIncr(DelayDbl &delay1,
const Delay &delay2,
const StaState *sta);
void
delayIncr(Delay &delay1,
float delay2,
const StaState *sta);
void
delayDecr(Delay &delay1,
const Delay &delay2,
const StaState *sta);
void
delayDecr(DelayDbl &delay1,
const Delay &delay2,
const StaState *sta);
Delay
delayProduct(const Delay &delay1,
float delay2,
const StaState *sta);
Delay
delayDiv(float delay1,
const Delay &delay2,
const StaState *sta);
const Delay &
delayInitValue(const MinMax *min_max);
bool
delayIsInitValue(const Delay &delay,
const MinMax *min_max);
bool
delayZero(const Delay &delay,
const StaState *sta);
bool
delayInf(const Delay &delay,
const StaState *sta);
bool
delayEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLess(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *sta);
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
// delay1-delay2 subtracting sigma instead of addiing.
Delay
delayRemove(const Delay &delay1,
const Delay &delay2);
} // namespace

View File

@ -1,152 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#pragma once
#include "MinMax.hh"
// Delay values defined as floats.
namespace sta {
class StaState;
using Delay = float;
// Delay double for accumulating Delays.
using DelayDbl = double;
const Delay delay_zero = 0.0;
void
initDelayConstants();
const char *
delayAsString(const Delay &delay,
const StaState *sta);
const char *
delayAsString(const Delay &delay,
const StaState *sta,
int digits);
const char *
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta,
int digits);
inline Delay
makeDelay(float delay,
float,
float)
{
return delay;
}
inline Delay
makeDelay2(float delay,
float,
float)
{
return delay;
}
inline float
delayAsFloat(const Delay &delay)
{
return delay;
}
// mean late+/early- sigma
inline float
delayAsFloat(const Delay &delay,
const EarlyLate *,
const StaState *)
{
return delay;
}
inline float
delaySigma2(const Delay &,
const EarlyLate *)
{
return 0.0;
}
const Delay &
delayInitValue(const MinMax *min_max);
bool
delayIsInitValue(const Delay &delay,
const MinMax *min_max);
bool
delayZero(const Delay &delay);
bool
delayInf(const Delay &delay);
bool
delayEqual(const Delay &delay1,
const Delay &delay2);
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
// delay1-delay2 subtracting sigma instead of addiing.
Delay
delayRemove(const Delay &delay1,
const Delay &delay2);
float
delayRatio(const Delay &delay1,
const Delay &delay2);
} // namespace

View File

@ -0,0 +1,90 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#pragma once
#include "Delay.hh"
namespace sta {
class DelayOpsNormal : public DelayOps
{
public:
float stdDev2(const Delay &delay,
const EarlyLate *early_late) const override;
float asFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta) const override;
double asFloat(const DelayDbl &delay,
const EarlyLate *early_late,
const StaState *sta) const override;
bool isZero(const Delay &delay) const override;
bool isInf(const Delay &delay) const override;
bool equal(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool less(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool less(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *sta) const override;
bool lessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool greater(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool greaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
Delay sum(const Delay &delay1,
const Delay &delay2) const override;
Delay sum(const Delay &delay1,
float delay2) const override;
Delay diff(const Delay &delay1,
const Delay &delay2) const override;
Delay diff(const Delay &delay1,
float delay2) const override;
Delay diff(float delay1,
const Delay &delay2) const override;
void incr(Delay &delay1,
const Delay &delay2) const override;
void incr(DelayDbl &delay1,
const Delay &delay2) const override;
void decr(Delay &delay1,
const Delay &delay2) const override;
void decr(DelayDbl &delay1,
const Delay &delay2) const override;
Delay product(const Delay &delay1,
float delay2) const override;
Delay div(float delay1,
const Delay &delay2) const override;
std::string asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const override;
};
} // namespace

View File

@ -1,203 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#pragma once
#include "MinMax.hh"
namespace sta {
class Delay;
class DelayDbl;
class StaState;
// Normal distribution with std deviation.
class Delay
{
public:
Delay();
Delay(const Delay &delay);
Delay(const DelayDbl &delay);
Delay(float mean);
Delay(float mean,
float sigma2);
float mean() const { return mean_; }
float sigma() const;
// sigma^2
float sigma2() const;
void operator=(const Delay &delay);
void operator=(float delay);
void operator+=(const Delay &delay);
void operator+=(float delay);
Delay operator+(const Delay &delay) const;
Delay operator+(float delay) const;
Delay operator-(const Delay &delay) const;
Delay operator-(float delay) const;
Delay operator-() const;
void operator-=(float delay);
void operator-=(const Delay &delay);
bool operator==(const Delay &delay) const;
private:
float mean_;
// Sigma^2
float sigma2_;
friend class DelayDbl;
};
// Dwlay with doubles for accumulating delays.
class DelayDbl
{
public:
DelayDbl();
float mean() const { return mean_; }
float sigma() const;
// sigma^2
float sigma2() const;
void operator=(float delay);
void operator+=(const Delay &delay);
void operator-=(const Delay &delay);
private:
double mean_;
// Sigma^2
double sigma2_;
friend class Delay;
};
const Delay delay_zero(0.0);
void
initDelayConstants();
const char *
delayAsString(const Delay &delay,
const StaState *sta);
const char *
delayAsString(const Delay &delay,
const StaState *sta,
int digits);
const char *
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta,
int digits);
Delay
makeDelay(float delay,
float sigma_early,
float sigma_late);
Delay
makeDelay2(float delay,
// sigma^2
float sigma_early,
float sigma_late);
inline float
delayAsFloat(const Delay &delay)
{
return delay.mean();
}
// mean late+/early- sigma
float
delayAsFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta);
float
delaySigma2(const Delay &delay,
const EarlyLate *early_late);
const Delay &
delayInitValue(const MinMax *min_max);
bool
delayIsInitValue(const Delay &delay,
const MinMax *min_max);
bool
delayZero(const Delay &delay);
bool
delayInf(const Delay &delay);
bool
delayEqual(const Delay &delay1,
const Delay &delay2);
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
// delay1-delay2 subtracting sigma instead of addiing.
Delay delayRemove(const Delay &delay1,
const Delay &delay2);
float
delayRatio(const Delay &delay1,
const Delay &delay2);
// Most non-operator functions on Delay are not defined as member
// functions so they can be defined on floats, where there is no class
// to define them.
Delay operator+(float delay1,
const Delay &delay2);
// Used for parallel gate delay calc.
Delay operator/(float delay1,
const Delay &delay2);
// Used for parallel gate delay calc.
Delay operator*(const Delay &delay1,
float delay2);
} // namespace

View File

@ -1,214 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#pragma once
#include "MinMax.hh"
namespace sta {
class Delay;
class DelayDbl;
class StaState;
// Normal distribution with early(left)/late(right) std deviations.
class Delay
{
public:
Delay();
Delay(const Delay &delay);
Delay(const DelayDbl &delay);
Delay(float mean);
Delay(float mean,
float sigma2_early,
float sigma2_late);
float mean() const { return mean_; }
float sigma(const EarlyLate *early_late) const;
// sigma^2
float sigma2(const EarlyLate *early_late) const;
float sigma2Early() const;
float sigma2Late() const;
void operator=(const Delay &delay);
void operator=(float delay);
void operator+=(const Delay &delay);
void operator+=(float delay);
Delay operator+(const Delay &delay) const;
Delay operator+(float delay) const;
Delay operator-(const Delay &delay) const;
Delay operator-(float delay) const;
Delay operator-() const;
void operator-=(float delay);
void operator-=(const Delay &delay);
bool operator==(const Delay &delay) const;
protected:
static const int early_index = 0;
static const int late_index = 1;
private:
float mean_;
// Sigma^2
float sigma2_[EarlyLate::index_count];
friend class DelayDbl;
};
// Dwlay with doubles for accumulating delays.
class DelayDbl
{
public:
DelayDbl();
float mean() const { return mean_; }
float sigma() const;
// sigma^2
float sigma2() const;
void operator=(float delay);
void operator+=(const Delay &delay);
void operator-=(const Delay &delay);
protected:
static const int early_index = 0;
static const int late_index = 1;
private:
double mean_;
// Sigma^2
double sigma2_[EarlyLate::index_count];
friend class Delay;
};
const Delay delay_zero(0.0);
void
initDelayConstants();
const char *
delayAsString(const Delay &delay,
const StaState *sta);
const char *
delayAsString(const Delay &delay,
const StaState *sta,
int digits);
const char *
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta,
int digits);
Delay
makeDelay(float delay,
float sigma_early,
float sigma_late);
Delay
makeDelay2(float delay,
// sigma^2
float sigma_early,
float sigma_late);
inline float
delayAsFloat(const Delay &delay)
{
return delay.mean();
}
// mean late+/early- sigma
float
delayAsFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta);
float
delaySigma2(const Delay &delay,
const EarlyLate *early_late);
const Delay &
delayInitValue(const MinMax *min_max);
bool
delayIsInitValue(const Delay &delay,
const MinMax *min_max);
bool
delayZero(const Delay &delay);
bool
delayInf(const Delay &delay);
bool
delayEqual(const Delay &delay1,
const Delay &delay2);
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLess(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayLessEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta);
bool
delayGreaterEqual(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
bool
delayGreater(const Delay &delay1,
const Delay &delay2,
const MinMax *min_max,
const StaState *sta);
// delay1-delay2 subtracting sigma instead of addiing.
Delay delayRemove(const Delay &delay1,
const Delay &delay2);
float
delayRatio(const Delay &delay1,
const Delay &delay2);
// Most non-operator functions on Delay are not defined as member
// functions so they can be defined on floats, where there is no class
// to define them.
Delay operator+(float delay1,
const Delay &delay2);
// Used for parallel gate delay calc.
Delay operator/(float delay1,
const Delay &delay2);
// Used for parallel gate delay calc.
Delay operator*(const Delay &delay1,
float delay2);
} // namespace

View File

@ -0,0 +1,90 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#pragma once
#include "Delay.hh"
namespace sta {
class DelayOpsScalar : public DelayOps
{
public:
float stdDev2(const Delay &delay,
const EarlyLate *early_late) const override;
float asFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta) const override;
double asFloat(const DelayDbl &delay,
const EarlyLate *early_late,
const StaState *sta) const override;
bool isZero(const Delay &delay) const override;
bool isInf(const Delay &delay) const override;
bool equal(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool less(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool less(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *sta) const override;
bool lessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool greater(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool greaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
Delay sum(const Delay &delay1,
const Delay &delay2) const override;
Delay sum(const Delay &delay1,
float delay2) const override;
Delay diff(const Delay &delay1,
const Delay &delay2) const override;
Delay diff(const Delay &delay1,
float delay2) const override;
Delay diff(float delay1,
const Delay &delay2) const override;
void incr(Delay &delay1,
const Delay &delay2) const override;
void incr(DelayDbl &delay1,
const Delay &delay2) const override;
void decr(Delay &delay1,
const Delay &delay2) const override;
void decr(DelayDbl &delay1,
const Delay &delay2) const override;
Delay product(const Delay &delay1,
float delay2) const override;
Delay div(float delay1,
const Delay &delay2) const override;
std::string asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const override;
};
} // namespace

View File

@ -0,0 +1,98 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#pragma once
#include "Delay.hh"
namespace sta {
class DelayOpsSkewNormal : public DelayOps
{
public:
float stdDev2(const Delay &delay,
const EarlyLate *early_late) const override;
float asFloat(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta) const override;
double asFloat(const DelayDbl &delay,
const EarlyLate *early_late,
const StaState *sta) const override;
bool isZero(const Delay &delay) const override;
bool isInf(const Delay &delay) const override;
bool equal(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool less(const Delay &delay1,
const Delay &delay2,
const StaState *sta) const override;
bool less(const DelayDbl &delay1,
const DelayDbl &delay2,
const StaState *sta) const override;
bool lessEqual(const Delay &delay1,
const Delay &delay2,
const StaState *) const override;
bool greater(const Delay &delay1,
const Delay &delay2,
const StaState *) const override;
bool greaterEqual(const Delay &delay1,
const Delay &delay2,
const StaState *) const override;
Delay sum(const Delay &delay1,
const Delay &delay2) const override;
Delay sum(const Delay &delay1,
float delay2) const override;
Delay diff(const Delay &delay1,
const Delay &delay2) const override;
Delay diff(const Delay &delay1,
float delay2) const override;
Delay diff(float delay1,
const Delay &delay2) const override;
void incr(Delay &delay1,
const Delay &delay2) const override;
void incr(DelayDbl &delay1,
const Delay &delay2) const override;
void decr(Delay &delay1,
const Delay &delay2) const override;
void decr(DelayDbl &delay1,
const Delay &delay2) const override;
Delay product(const Delay &delay1,
float delay2) const override;
Delay div(float delay1,
const Delay &delay2) const override;
std::string asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const override;
private:
float skewnessSum(const Delay &delay1,
const Delay &delay2) const;
double skewnessSum(double std_dev1,
double skewness1,
double std_dev2,
double skewness2) const;
};
} // namespace

View File

@ -25,6 +25,7 @@
#pragma once
#include <exception>
#include <string>
#include "Report.hh"
@ -42,7 +43,7 @@ public:
class ExceptionMsg : public Exception
{
public:
ExceptionMsg(const char *msg,
ExceptionMsg(const std::string &msg,
const bool suppressed);
virtual const char *what() const noexcept;
virtual bool suppressed() const { return suppressed_; }
@ -55,11 +56,11 @@ private:
class ExceptionLine : public Exception
{
public:
ExceptionLine(const char *filename,
ExceptionLine(const std::string &filename,
int line);
protected:
const char *filename_;
std::string filename_;
int line_;
};
@ -67,29 +68,31 @@ protected:
class FileNotReadable : public Exception
{
public:
FileNotReadable(const char *filename);
FileNotReadable(std::string filename);
virtual const char *what() const noexcept;
protected:
const char *filename_;
std::string filename_;
std::string msg_;
};
// Failure opening filename for writing.
class FileNotWritable : public Exception
{
public:
FileNotWritable(const char *filename);
FileNotWritable(std::string filename);
virtual const char *what() const noexcept;
protected:
const char *filename_;
std::string filename_;
std::string msg_;
};
// Report an error condition that should not be possible.
// The default handler prints msg to stderr and exits.
// The msg should NOT include a period or return.
// Only for use in those cases where a Report object is not available.
#define criticalError(id,msg) \
// Only for use in those cases where a Report object is not available.
#define criticalError(id, msg) \
Report::defaultReport()->fileCritical(id, __FILE__, __LINE__, msg)
} // namespace

View File

@ -24,6 +24,7 @@
#pragma once
#include <string>
#include <vector>
#include "Error.hh"
@ -67,7 +68,7 @@ public:
virtual bool isGroupPath() const { return false; }
virtual bool isFilter() const { return false; }
virtual ExceptionPathType type() const = 0;
virtual const char *asString(const Network *network) const;
virtual std::string to_string(const Network *network) const;
ExceptionFrom *from() const { return from_; }
ExceptionThruSeq *thrus() const { return thrus_; }
ExceptionTo *to() const { return to_; }
@ -127,14 +128,14 @@ public:
virtual bool useEndClk() const { return false; }
virtual int pathMultiplier() const { return 0; }
virtual float delay() const { return 0.0; }
virtual const char *name() const { return nullptr; }
virtual std::string name() const { return ""; }
virtual bool isDefault() const { return false; }
virtual bool ignoreClkLatency() const { return false; }
virtual bool breakPath() const { return false; }
protected:
virtual const char *typeString() const = 0;
const char *fromThruToString(const Network *network) const;
std::string fromThruToString(const Network *network) const;
void makeStates();
ExceptionFrom *from_;
@ -209,7 +210,7 @@ public:
bool own_pts) override;
bool isPathDelay() const override { return true; }
ExceptionPathType type() const override { return ExceptionPathType::path_delay; }
const char *asString(const Network *network) const override;
std::string to_string(const Network *network) const override;
const char *typeString() const override;
bool mergeable(ExceptionPath *exception) const override;
bool overrides(ExceptionPath *exception) const override;
@ -245,7 +246,7 @@ public:
ExceptionPathType type() const override { return ExceptionPathType::multi_cycle; }
bool matches(const MinMax *min_max,
bool exactly) const override;
const char *asString(const Network *network) const override;
std::string to_string(const Network *network) const override;
const char *typeString() const override;
bool mergeable(ExceptionPath *exception) const override;
bool overrides(ExceptionPath *exception) const override;
@ -292,7 +293,7 @@ public:
class GroupPath : public ExceptionPath
{
public:
GroupPath(const char *name,
GroupPath(const std::string &name,
bool is_default,
ExceptionFrom *from,
ExceptionThruSeq *thrus,
@ -311,11 +312,11 @@ public:
bool overrides(ExceptionPath *exception) const override;
int typePriority() const override;
bool tighterThan(ExceptionPath *exception) const override;
const char *name() const override { return name_; }
std::string name() const override { return name_; }
bool isDefault() const override { return is_default_; }
protected:
const char *name_;
std::string name_;
bool is_default_;
};
@ -343,7 +344,7 @@ public:
// All pins and instance/net pins.
virtual PinSet allPins(const Network *network) = 0;
virtual int typePriority() const = 0;
virtual const char *asString(const Network *network) const = 0;
virtual std::string to_string(const Network *network) const = 0;
virtual size_t objectCount() const = 0;
virtual void addPin(const Pin *pin,
const Network *network) = 0;
@ -367,8 +368,8 @@ protected:
// exception merging.
size_t hash_;
// Maximum number of objects for asString() to show.
static const int as_string_max_objects_;
// Maximum number of objects for to_string() to show.
static const int to_string_max_objects_;
static const size_t hash_clk = 3;
static const size_t hash_pin = 5;
static const size_t hash_net = 7;
@ -402,7 +403,7 @@ public:
const Network *network) const override;
void mergeInto(ExceptionPt *pt,
const Network *network) override;
const char *asString(const Network *network) const override;
std::string to_string(const Network *network) const override;
size_t objectCount() const override;
void deleteClock(Clock *clk);
void addPin(const Pin *pin,
@ -467,7 +468,7 @@ public:
const Network *network);
ExceptionTo *clone(const Network *network);
bool isTo() const override { return true; }
const char *asString(const Network *network) const override;
std::string to_string(const Network *network) const override;
const RiseFallBoth *endTransition() { return end_rf_; }
bool intersectsPts(ExceptionTo *to,
const Network *network) const;
@ -512,7 +513,7 @@ public:
const Network *network);
~ExceptionThru();
ExceptionThru *clone(const Network *network);
const char *asString(const Network *network) const override;
std::string to_string(const Network *network) const override;
bool isThru() const override { return true; }
PinSet *pins() override { return pins_; }
EdgePinsSet *edges() override { return edges_; }

150
include/sta/Format.hh Normal file
View File

@ -0,0 +1,150 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#pragma once
#include <cstdio>
#include <fstream>
#include <string>
#include <string_view>
#include "StaConfig.hh"
#ifdef ZLIB_FOUND
#include <zlib.h>
#endif
#if HAVE_CXX_STD_FORMAT
#include <format>
namespace sta {
template <typename... Args>
std::string format(std::format_string<Args...> fmt,
Args &&...args) {
return std::format(fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void print(std::ofstream &stream,
std::format_string<Args...> fmt,
Args &&...args) {
stream << std::format(fmt, std::forward<Args>(args)...);
}
#ifdef ZLIB_FOUND
template <typename... Args>
void print(gzFile stream,
std::format_string<Args...> fmt,
Args &&...args) {
std::string s = sta::format(fmt, std::forward<Args>(args)...);
gzwrite(stream, s.c_str(), s.size());
}
#endif
template <typename... Args>
void print(FILE *stream,
std::format_string<Args...> fmt,
Args &&...args) {
std::string s = sta::format(fmt, std::forward<Args>(args)...);
std::fprintf(stream, "%s", s.c_str());
}
inline std::string vformat(std::string_view fmt,
std::format_args args) {
return std::vformat(fmt, args);
}
template <typename... Args>
auto make_format_args(Args &&...args) {
return std::make_format_args(std::forward<Args>(args)...);
}
// Format with runtime format string - captures args to avoid make_format_args
// rvalue reference issues.
template <typename... Args>
std::string formatRuntime(std::string_view fmt,
Args &&...args) {
auto args_tuple = std::make_tuple(std::forward<Args>(args)...);
return std::apply(
[fmt](auto &...a) {
return std::vformat(fmt, std::make_format_args(a...));
},
args_tuple);
}
} // namespace sta
#else
#include <fmt/core.h>
namespace sta {
template <typename... Args>
std::string format(fmt::format_string<Args...> fmt,
Args &&...args) {
return fmt::format(fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void print(std::ofstream &stream,
fmt::format_string<Args...> fmt,
Args &&...args) {
stream << fmt::format(fmt, std::forward<Args>(args)...);
}
#ifdef ZLIB_FOUND
template <typename... Args>
void print(gzFile stream,
fmt::format_string<Args...> fmt,
Args &&...args) {
std::string s = sta::format(fmt, std::forward<Args>(args)...);
gzwrite(stream, s.c_str(), s.size());
}
#endif
template <typename... Args>
void print(FILE *stream,
fmt::format_string<Args...> fmt,
Args &&...args) {
std::string s = sta::format(fmt, std::forward<Args>(args)...);
std::fprintf(stream, "%s", s.c_str());
}
inline
std::string vformat(std::string_view fmt,
fmt::format_args args) {
return fmt::vformat(fmt, args);
}
template <typename... Args>
auto make_format_args(Args &&...args) {
return fmt::make_format_args(std::forward<Args>(args)...);
}
template <typename... Args>
std::string formatRuntime(std::string_view fmt,
Args &&...args) {
return fmt::format(fmt::runtime(fmt), std::forward<Args>(args)...);
}
} // namespace sta
#endif

View File

@ -58,13 +58,7 @@ static constexpr ObjectIdx vertex_idx_null = object_idx_null;
class Graph : public StaState
{
public:
// slew_rf_count is
// 0 no slews
// 1 one slew for rise/fall
// 2 rise/fall slews
// ap_count is the dcalc analysis point count.
Graph(StaState *sta,
int slew_rf_count,
DcalcAPIndex ap_count);
void makeGraph();
~Graph();
@ -97,9 +91,11 @@ public:
// Reported slew are the same as those in the liberty tables.
// reported_slews = measured_slews / slew_derate_from_library
// Measured slews are between slew_lower_threshold and slew_upper_threshold.
const Slew &slew(const Vertex *vertex,
const RiseFall *rf,
DcalcAPIndex ap_index);
Slew slew(const Vertex *vertex,
const RiseFall *rf,
DcalcAPIndex ap_index);
Slew slew(const Vertex *vertex,
size_t index);
void setSlew(Vertex *vertex,
const RiseFall *rf,
DcalcAPIndex ap_index,
@ -134,11 +130,11 @@ public:
void setArcDelay(Edge *edge,
const TimingArc *arc,
DcalcAPIndex ap_index,
ArcDelay delay);
const ArcDelay &delay);
// Alias for arcDelays using library wire arcs.
const ArcDelay &wireArcDelay(const Edge *edge,
const RiseFall *rf,
DcalcAPIndex ap_index);
ArcDelay wireArcDelay(const Edge *edge,
const RiseFall *rf,
DcalcAPIndex ap_index);
void setWireArcDelay(Edge *edge,
const RiseFall *rf,
DcalcAPIndex ap_index,
@ -222,7 +218,6 @@ protected:
// driver/source (top level input, instance pin output) vertex
// in pin_bidirect_drvr_vertex_map
PinVertexMap pin_bidirect_drvr_vertex_map_;
int slew_rf_count_;
DcalcAPIndex ap_count_;
// Sdf period check annotations.
PeriodCheckAnnotations *period_check_annotations_;
@ -258,8 +253,6 @@ public:
[[nodiscard]] bool isRoot() const{ return level_ == 0; }
[[nodiscard]] bool hasFanin() const;
[[nodiscard]] bool hasFanout() const;
Slew *slews() { return slews_; }
const Slew *slews() const { return slews_; }
Path *paths() const { return paths_; }
Path *makePaths(uint32_t count);
void setPaths(Path *paths);
@ -298,14 +291,18 @@ protected:
bool is_bidirect_drvr,
bool is_reg_clk);
void clear();
void setSlews(Slew *slews);
Slew *slews() { return std::bit_cast<Slew*>(slews_); }
const Slew *slews() const { return std::bit_cast<const Slew*>(slews_); }
float *slewsFloat() { return slews_; }
const float *slewsFloat() const { return slews_; }
void setSlews(float *slews);
Pin *pin_;
EdgeId in_edges_; // Edges to this vertex.
EdgeId out_edges_; // Edges from this vertex.
// Delay calc
Slew *slews_;
float *slews_;
// Search
Path *paths_;
@ -356,8 +353,9 @@ public:
TimingSense sense() const;
TimingArcSet *timingArcSet() const { return arc_set_; }
void setTimingArcSet(TimingArcSet *set);
ArcDelay *arcDelays() const { return arc_delays_; }
void setArcDelays(ArcDelay *arc_delays);
float *arcDelays() { return arc_delays_; }
const float *arcDelays() const { return arc_delays_; }
void setArcDelays(float *delays);
bool delay_Annotation_Is_Incremental() const {return delay_annotation_is_incremental_;};
void setDelayAnnotationIsIncremental(bool is_incr);
// Edge is disabled to break combinational loops.
@ -398,7 +396,7 @@ protected:
EdgeId vertex_in_link_; // Vertex in edges list.
EdgeId vertex_out_next_; // Vertex out edges doubly linked list.
EdgeId vertex_out_prev_;
ArcDelay *arc_delays_;
float *arc_delays_;
union {
uintptr_t bits_;
std::vector<bool> *seq_;

View File

@ -245,8 +245,8 @@ protected:
bool annotateDelaySlew(Edge *edge,
const TimingArc *arc,
ArcDelay &gate_delay,
Slew &gate_slew,
const ArcDelay &gate_delay,
const Slew &gate_slew,
const Scene *scene,
const MinMax *min_max);
bool annotateLoadDelays(Vertex *drvr_vertex,

View File

@ -33,44 +33,11 @@
namespace sta {
class InternalPowerModel;
using InternalPowerModels =
std::array<std::shared_ptr<InternalPowerModel>, RiseFall::index_count>;
class InternalPower
{
public:
InternalPower(LibertyPort *port,
LibertyPort *related_port,
LibertyPort *related_pg_pin,
const std::shared_ptr<FuncExpr> &when,
InternalPowerModels &models);
//InternalPower(InternalPower &&other) noexcept;
LibertyCell *libertyCell() const;
LibertyPort *port() const { return port_; }
LibertyPort *relatedPort() const { return related_port_; }
FuncExpr *when() const { return when_.get(); }
LibertyPort *relatedPgPin() const { return related_pg_pin_; }
float power(const RiseFall *rf,
const Pvt *pvt,
float in_slew,
float load_cap) const;
const InternalPowerModel *model(const RiseFall *rf) const;
protected:
LibertyPort *port_;
LibertyPort *related_port_;
LibertyPort *related_pg_pin_;
std::shared_ptr<FuncExpr> when_;
InternalPowerModels models_;
};
class InternalPowerModel
{
public:
InternalPowerModel(TableModel *model);
~InternalPowerModel();
InternalPowerModel();
InternalPowerModel(std::shared_ptr<TableModel> model);
float power(const LibertyCell *cell,
const Pvt *pvt,
float in_slew,
@ -80,7 +47,7 @@ public:
float in_slew,
float load_cap,
int digits) const;
const TableModel *model() const { return model_; }
const TableModel *model() const { return model_.get(); }
protected:
void findAxisValues(float in_slew,
@ -95,7 +62,36 @@ protected:
bool checkAxes(const TableModel *model);
bool checkAxis(const TableAxis *axis);
TableModel *model_;
std::shared_ptr<TableModel> model_;
};
using InternalPowerModels = std::array<InternalPowerModel, RiseFall::index_count>;
class InternalPower
{
public:
InternalPower(LibertyPort *port,
LibertyPort *related_port,
LibertyPort *related_pg_pin,
const std::shared_ptr<FuncExpr> &when,
const InternalPowerModels &models);
LibertyCell *libertyCell() const;
LibertyPort *port() const { return port_; }
LibertyPort *relatedPort() const { return related_port_; }
FuncExpr *when() const { return when_.get(); }
LibertyPort *relatedPgPin() const { return related_pg_pin_; }
float power(const RiseFall *rf,
const Pvt *pvt,
float in_slew,
float load_cap) const;
const InternalPowerModel &model(const RiseFall *rf) const;
protected:
LibertyPort *port_;
LibertyPort *related_port_;
LibertyPort *related_pg_pin_;
std::shared_ptr<FuncExpr> when_;
InternalPowerModels models_;
};
} // namespace

View File

@ -589,7 +589,7 @@ public:
LibertyPort *related_port,
LibertyPort *related_pg_pin,
const std::shared_ptr<FuncExpr> &when,
InternalPowerModels &models);
const InternalPowerModels &models);
void makeLeakagePower(LibertyPort *related_pg_port,
FuncExpr *when,
float power);

View File

@ -37,14 +37,22 @@ public:
void gateDelay(const Pvt *pvt,
float in_slew,
float load_cap,
bool pocv_enabled,
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew) const override;
float &gate_delay,
float &drvr_slew) const override;
void gateDelayPocv(const Pvt *pvt,
float in_slew,
float load_cap,
const MinMax *min_max,
PocvMode pocv_mode,
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew) const override;
std::string reportGateDelay(const Pvt *pvt,
float in_slew,
float load_cap,
bool pocv_enabled,
const MinMax *min_max,
PocvMode pocv_mode,
int digits) const override;
float driveResistance(const Pvt *pvt) const override;
@ -64,13 +72,15 @@ public:
float from_slew,
float to_slew,
float related_out_cap,
bool pocv_enabled) const override;
const MinMax *min_max,
PocvMode pocv_mode) const override;
std::string reportCheckDelay(const Pvt *pvt,
float from_slew,
const char *from_slew_annotation,
float to_slew,
float related_out_cap,
bool pocv_enabled,
const MinMax *min_max,
PocvMode pocv_mode,
int digits) const override;
protected:

View File

@ -25,12 +25,13 @@
#pragma once
#include <string>
#include <string_view>
namespace sta {
// Return true if name is a bus.
bool
isBusName(const char *name,
isBusName(std::string_view name,
const char brkt_left,
const char brkt_right,
char escape);
@ -43,7 +44,7 @@ isBusName(const char *name,
// index = bit
// Caller must delete returned bus_name string.
void
parseBusName(const char *name,
parseBusName(std::string_view name,
const char brkt_left,
const char brkt_right,
char escape,
@ -53,9 +54,9 @@ parseBusName(const char *name,
int &index);
// Allow multiple different left/right bus brackets.
void
parseBusName(const char *name,
const char *brkts_left,
const char *brkts_right,
parseBusName(std::string_view name,
std::string_view brkts_left,
std::string_view brkts_right,
char escape,
// Return values.
bool &is_bus,
@ -66,7 +67,7 @@ parseBusName(const char *name,
// bus_name is set to null if name is not a range.
// Caller must delete returned bus_name string.
void
parseBusName(const char *name,
parseBusName(std::string_view name,
const char brkt_left,
const char brkt_right,
char escape,
@ -81,9 +82,9 @@ parseBusName(const char *name,
// brkt_lefts and brkt_rights are corresponding strings of legal
// bus brackets such as "[(<" and "])>".
void
parseBusName(const char *name,
const char *brkts_left,
const char *brkts_right,
parseBusName(std::string_view name,
std::string_view brkts_left,
std::string_view brkts_right,
const char escape,
// Return values.
bool &is_bus,
@ -95,7 +96,7 @@ parseBusName(const char *name,
// Insert escapes before ch1 and ch2 in token.
std::string
escapeChars(const char *token,
escapeChars(std::string_view token,
const char ch1,
const char ch2,
const char escape);

View File

@ -45,14 +45,14 @@ public:
const StaState *sta);
Path(Vertex *vertex,
Tag *tag,
Arrival arrival,
const Arrival &arrival,
Path *prev_path,
Edge *prev_edge,
TimingArc *prev_arc,
const StaState *sta);
Path(Vertex *vertex,
Tag *tag,
Arrival arrival,
const Arrival &arrival,
Path *prev_path,
Edge *prev_edge,
TimingArc *prev_arc,
@ -62,11 +62,11 @@ public:
bool isNull() const;
// prev_path null
void init(Vertex *vertex,
Arrival arrival,
const Arrival &arrival,
const StaState *sta);
void init(Vertex *vertex,
Tag *tag,
Arrival arrival,
const Arrival &arrival,
Path *prev_path,
Edge *prev_edge,
TimingArc *prev_arc,
@ -76,7 +76,7 @@ public:
const StaState *sta);
void init(Vertex *vertex,
Tag *tag,
Arrival arrival,
const Arrival &arrival,
const StaState *sta);
Vertex *vertex(const StaState *sta) const;
@ -98,14 +98,12 @@ public:
const MinMax *minMax(const StaState *sta) const;
PathAPIndex pathAnalysisPtIndex(const StaState *sta) const;
DcalcAPIndex dcalcAnalysisPtIndex(const StaState *sta) const;
Arrival &arrival() { return arrival_; }
const Arrival &arrival() const { return arrival_; }
void setArrival(Arrival arrival);
Required &required() { return required_; }
const Required &required() const {return required_; }
void setRequired(const Required &required);
Slack slack(const StaState *sta) const;
Slew slew(const StaState *sta) const;
const Slew slew(const StaState *sta) const;
// This takes the same time as prevPath and prevArc combined.
Path *prevPath() const;
void setPrevPath(Path *prev_path);

View File

@ -98,7 +98,7 @@ public:
virtual const char *typeName() const = 0;
virtual int exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const;
virtual Arrival dataArrivalTime(const StaState *sta) const;
virtual const Arrival &dataArrivalTime(const StaState *sta) const;
// Arrival time with source clock offset.
Arrival dataArrivalTimeOffset(const StaState *sta) const;
virtual Required requiredTime(const StaState *sta) const = 0;
@ -270,11 +270,6 @@ public:
protected:
PathEndClkConstrained(Path *path,
Path *clk_path);
PathEndClkConstrained(Path *path,
Path *clk_path,
Crpr crpr,
bool crpr_valid);
float sourceClkOffset(const ClockEdge *src_clk_edge,
const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
@ -300,11 +295,6 @@ protected:
PathEndClkConstrainedMcp(Path *path,
Path *clk_path,
MultiCyclePath *mcp);
PathEndClkConstrainedMcp(Path *path,
Path *clk_path,
MultiCyclePath *mcp,
Crpr crpr,
bool crpr_valid);
float checkMcpAdjustment(const Path *path,
const ClockEdge *tgt_clk_edge,
const StaState *sta) const;
@ -341,13 +331,6 @@ public:
virtual Delay clkSkew(const StaState *sta);
protected:
PathEndCheck(Path *path,
TimingArc *check_arc,
Edge *check_edge,
Path *clk_path,
MultiCyclePath *mcp,
Crpr crpr,
bool crpr_valid);
Delay sourceClkDelay(const StaState *sta) const;
virtual Required requiredTimeNoCrpr(const StaState *sta) const;
@ -404,18 +387,6 @@ public:
virtual bool ignoreClkLatency(const StaState *sta) const;
protected:
PathEndLatchCheck(Path *path,
TimingArc *check_arc,
Edge *check_edge,
Path *clk_path,
Path *disable,
MultiCyclePath *mcp,
PathDelay *path_delay,
Delay src_clk_arrival,
Crpr crpr,
bool crpr_valid);
private:
Path *disable_path_;
PathDelay *path_delay_;
// Source clk arrival for set_max_delay -ignore_clk_latency.
@ -450,12 +421,6 @@ public:
const StaState *sta) const;
protected:
PathEndOutputDelay(OutputDelay *output_delay,
Path *path,
Path *clk_path,
MultiCyclePath *mcp,
Crpr crpr,
bool crpr_valid);
Arrival tgtClkDelay(const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
const StaState *sta) const;
@ -491,14 +456,6 @@ public:
const StaState *sta) const;
protected:
PathEndGatedClock(Path *gating_ref,
Path *clk_path,
const TimingRole *check_role,
MultiCyclePath *mcp,
ArcDelay margin,
Crpr crpr,
bool crpr_valid);
const TimingRole *check_role_;
ArcDelay margin_;
};
@ -525,20 +482,12 @@ public:
virtual const Path *dataClkPath() const { return data_clk_path_; }
protected:
PathEndDataCheck(DataCheck *check,
Path *data_path,
Path *data_clk_path,
Path *clk_path,
MultiCyclePath *mcp,
Crpr crpr,
bool crpr_valid);
Path *clkPath(Path *path,
const StaState *sta);
Arrival requiredTimeNoCrpr(const StaState *sta) const;
// setup uses zero cycle default
virtual int setupDefaultCycles() const { return 0; }
private:
Path *data_clk_path_;
DataCheck *check_;
};
@ -588,15 +537,6 @@ public:
virtual bool ignoreClkLatency(const StaState *sta) const;
protected:
PathEndPathDelay(PathDelay *path_delay,
Path *path,
Path *clk_path,
TimingArc *check_arc,
Edge *check_edge,
OutputDelay *output_delay,
Arrival src_clk_arrival,
Crpr crpr,
bool crpr_valid);
void findSrcClkArrival(const StaState *sta);
PathDelay *path_delay_;

View File

@ -29,7 +29,6 @@
#include <map>
#include <mutex>
#include "BoundedHeap.hh"
#include "SdcClass.hh"
#include "StaState.hh"
#include "SearchClass.hh"
@ -43,7 +42,7 @@ class PathEndVisitor;
using PathGroupIterator = PathEndSeq::iterator;
using PathGroupClkMap = std::map<const Clock*, PathGroup*>;
using PathGroupNamedMap = std::map<const char*, PathGroup*, CharPtrLess>;
using PathGroupNamedMap = std::map<std::string, PathGroup*>;
using PathGroupSeq = std::vector<PathGroup*>;
// A collection of PathEnds grouped and sorted for reporting.
@ -70,7 +69,7 @@ public:
~PathGroup();
const std::string &name() const { return name_; }
const MinMax *minMax() const { return min_max_;}
PathEndSeq pathEnds() const;
PathEndSeq pathEnds() const { return path_ends_; }
void insert(PathEnd *path_end);
// Push group_path_count into path_ends.
void pushEnds(PathEndSeq &path_ends);
@ -93,6 +92,9 @@ protected:
bool cmp_slack,
const MinMax *min_max,
const StaState *sta);
void ensureSortedMaxPaths();
void prune();
void sort();
std::string name_;
int group_path_count_;
@ -101,9 +103,11 @@ protected:
bool unique_edges_;
float slack_min_;
float slack_max_;
PathEndSeq path_ends_;
const MinMax *min_max_;
bool cmp_slack_;
BoundedHeap<PathEnd*, PathEndLess> heap_;
float threshold_;
std::mutex lock_;
const StaState *sta_;
};
@ -136,7 +140,7 @@ public:
bool unconstrained_paths,
// Return value.
PathEndSeq &path_ends);
PathGroup *findPathGroup(const char *name,
PathGroup *findPathGroup(const std::string &name,
const MinMax *min_max) const;
PathGroup *findPathGroup(const Clock *clock,
const MinMax *min_max) const;
@ -187,7 +191,7 @@ protected:
bool gated_clk,
bool unconstrained,
const MinMax *min_max);
bool reportGroup(const char *group_name,
bool reportGroup(const std::string &group_name,
StringSet &group_names) const;
static GroupPath *groupPathTo(const PathEnd *path_end,
const StaState *sta);

View File

@ -22,19 +22,15 @@
//
// This notice may not be removed or altered from any source distribution.
#include "Machine.hh"
#include "StringUtil.hh"
#include "Units.hh"
#include "StaState.hh"
#include "Delay.hh"
#pragma once
namespace sta {
enum class PocvMode { scalar, normal, skew_normal };
const char *
delayAsString(const Delay &delay,
const StaState *sta)
{
return delayAsString(delay, sta, sta->units()->timeUnit()->digits());
}
pocvModeName(PocvMode mode);
PocvMode
findPocvMode(const char *mode_name);
} // namespace

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#pragma once
@ -27,15 +27,23 @@
#include <stdio.h>
#include <cstdarg>
#include <string>
#include <string_view>
#include <mutex>
#include <set>
#include "Machine.hh" // __attribute__
#include "Machine.hh" // __attribute__
#include "Format.hh"
struct Tcl_Interp;
namespace sta {
// Throws ExceptionMsg - implemented in Report.cc to avoid circular include with
// Error.hh
void
reportThrowExceptionMsg(const std::string &msg,
bool suppressed);
// Output streams used for printing.
// This is a wrapper for all printing. It supports logging output to
// a file and redirection of command output to a file.
@ -45,74 +53,137 @@ public:
Report();
virtual ~Report();
// Print line with return.
virtual void reportLine(const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
virtual void reportLineString(const char *line);
virtual void reportLineString(const std::string &line);
virtual void reportLine(const std::string &line);
virtual void reportBlankLine();
// Print formatted line using std::format (C++20).
template <typename... Args>
void report(std::string_view fmt,
Args &&...args)
{
reportMsg(sta::vformat(fmt, sta::make_format_args(args...)));
}
virtual void reportMsg(const std::string &formatted_msg)
{
reportLine(formatted_msg);
}
////////////////////////////////////////////////////////////////
// Report warning.
virtual void warn(int id,
const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
virtual void vwarn(int id,
const char *fmt,
va_list args);
template <typename... Args>
void warn(int id,
std::string_view fmt,
Args &&...args)
{
if (!isSuppressed(id))
warnMsg(id, sta::vformat(fmt, sta::make_format_args(args...)));
}
virtual void warnMsg(int id,
const std::string &formatted_msg) {
reportLine(sta::format("Warning {}: {}", id, formatted_msg));
}
// Report warning in a file.
virtual void fileWarn(int id,
const char *filename,
int line,
const char *fmt, ...)
__attribute__((format (printf, 5, 6)));
virtual void vfileWarn(int id,
const char *filename,
int line,
const char *fmt,
va_list args);
template <typename... Args>
void fileWarn(int id,
std::string_view filename,
int line,
std::string_view fmt,
Args &&...args)
{
if (!isSuppressed(id)) {
fileWarnMsg(id, filename, line,
sta::vformat(fmt, sta::make_format_args(args...)));
}
}
virtual void
fileWarnMsg(int id,
std::string_view filename,
int line,
const std::string &formatted_msg) {
reportLine(sta::format("Warning {}: {} line {}, {}",
id, filename, line, formatted_msg));
}
virtual void error(int id,
const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
virtual void verror(int id,
const char *fmt,
va_list args);
template <typename... Args>
void error(int id,
std::string_view fmt,
Args &&...args)
{
errorMsg(id, sta::vformat(fmt, sta::make_format_args(args...)));
}
virtual void errorMsg(int id,
const std::string &formatted_msg)
{
reportThrowExceptionMsg(sta::format("{} {}", id, formatted_msg), isSuppressed(id));
}
// Report error in a file.
virtual void fileError(int id,
const char *filename,
int line,
const char *fmt, ...)
__attribute__((format (printf, 5, 6)));
virtual void vfileError(int id,
const char *filename,
int line,
const char *fmt,
va_list args);
template <typename... Args>
void fileError(int id,
std::string_view filename,
int line,
std::string_view fmt,
Args &&...args)
{
fileErrorMsg(id, filename, line,
sta::vformat(fmt, sta::make_format_args(args...)));
}
virtual void fileErrorMsg(int id,
std::string_view filename,
int line,
const std::string &formatted_msg)
{
reportThrowExceptionMsg(sta::format("{} {} line {}, {}",
id, filename, line, formatted_msg),
isSuppressed(id));
}
// Critical.
// Critical.
// Report error condition that should not be possible or that prevents execution.
// The default handler prints msg to stderr and exits.
virtual void critical(int id,
const char *fmt,
...)
__attribute__((format (printf, 3, 4)));
virtual void fileCritical(int id,
const char *filename,
int line,
const char *fmt,
...)
__attribute__((format (printf, 5, 6)));
template <typename... Args>
void critical(int id,
std::string_view fmt,
Args &&...args)
{
criticalMsg(id, sta::vformat(fmt, sta::make_format_args(args...)));
}
virtual void criticalMsg(int id,
const std::string &formatted_msg)
{
reportLine(sta::format("Critical {}: {}", id, formatted_msg));
exit(1);
}
template <typename... Args>
void fileCritical(int id,
std::string_view filename,
int line,
std::string_view fmt,
Args &&...args)
{
fileCriticalMsg(id, filename, line,
sta::vformat(fmt, sta::make_format_args(args...)));
}
virtual void fileCriticalMsg(int id,
std::string_view filename,
int line,
const std::string &formatted_msg)
{
reportLine(sta::format("Critical {}: {} line {}, {}", id, filename, line,
formatted_msg));
exit(1);
}
// Log output to filename until logEnd is called.
virtual void logBegin(const char *filename);
virtual void logBegin(std::string filename);
virtual void logEnd();
// Redirect output to filename until redirectFileEnd is called.
virtual void redirectFileBegin(const char *filename);
virtual void redirectFileBegin(std::string filename);
// Redirect append output to filename until redirectFileEnd is called.
virtual void redirectFileAppendBegin(const char *filename);
virtual void redirectFileAppendBegin(std::string filename);
virtual void redirectFileEnd();
// Redirect output to a string until redirectStringEnd is called.
virtual void redirectStringBegin();
@ -139,9 +210,7 @@ protected:
// Return the number of characters written.
virtual size_t printConsole(const char *buffer,
size_t length);
void printToBuffer(const char *fmt,
...)
__attribute__((format (printf, 2, 3)));
void printToBuffer(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void printToBuffer(const char *fmt,
va_list args);
@ -169,4 +238,4 @@ protected:
friend class Debug;
};
} // namespace
} // namespace sta

View File

@ -44,20 +44,20 @@ class ReportTcl : public Report
public:
ReportTcl();
virtual ~ReportTcl();
virtual void logBegin(const char *filename);
virtual void logEnd();
virtual void redirectFileBegin(const char *filename);
virtual void redirectFileAppendBegin(const char *filename);
virtual void redirectFileEnd();
virtual void redirectStringBegin();
virtual const char *redirectStringEnd();
void logBegin(std::string filename) override;
void logEnd() override;
void redirectFileBegin(std::string filename) override;
void redirectFileAppendBegin(std::string filename) override;
void redirectFileEnd() override;
void redirectStringBegin() override;
const char *redirectStringEnd() override;
// This must be called after the Tcl interpreter has been constructed.
// It makes the encapsulated channels.
virtual void setTclInterp(Tcl_Interp *interp);
void setTclInterp(Tcl_Interp *interp) override;
protected:
virtual size_t printConsole(const char *buffer,
size_t length);
size_t printConsole(const char *buffer,
size_t length) override;
void flush();
private:

View File

@ -179,11 +179,11 @@ using InstDeratingFactorsMap = std::map<const Instance*, DeratingFactorsCell*>;
using CellDeratingFactorsMap = std::map<const LibertyCell*, DeratingFactorsCell*>;
using ClockGroupsSet = std::set<ClockGroups*>;
using ClockGroupsClkMap = std::map<const Clock*, ClockGroupsSet*>;
using ClockGroupsNameMap = std::map<const char*, ClockGroups*, CharPtrLess>;
using ClockGroupsNameMap = std::map<std::string, ClockGroups*>;
using ClockSenseMap = std::map<PinClockPair, ClockSense, PinClockPairLess>;
using ClkHpinDisables = std::set<ClkHpinDisable*, ClkHpinDisableLess>;
using GroupPathSet = std::set<GroupPath*, ExceptionPathLess>;
using GroupPathMap = std::map<const char*, GroupPathSet*, CharPtrLess>;
using GroupPathMap = std::map<std::string, GroupPathSet*>;
using ClockPairSet = std::set<ClockPair, ClockPairLess>;
using NetVoltageMap = std::map<const Net*, MinMaxFloatValues>;
@ -499,7 +499,7 @@ public:
Clock *to_clk,
const RiseFallBoth *to_rf,
const SetupHoldAll *setup_hold);
ClockGroups *makeClockGroups(const char *name,
ClockGroups *makeClockGroups(const std::string &name,
bool logically_exclusive,
bool physically_exclusive,
bool asynchronous,
@ -507,11 +507,13 @@ public:
const char *comment);
void makeClockGroup(ClockGroups *clk_groups,
ClockSet *clks);
void removeClockGroups(const char *name);
// nullptr name removes all.
void removeClockGroupsLogicallyExclusive(const char *name);
void removeClockGroupsPhysicallyExclusive(const char *name);
void removeClockGroupsAsynchronous(const char *name);
void removeClockGroups(const std::string &name);
void removeClockGroupsLogicallyExclusive();
void removeClockGroupsLogicallyExclusive(const std::string &name);
void removeClockGroupsPhysicallyExclusive();
void removeClockGroupsPhysicallyExclusive(const std::string &name);
void removeClockGroupsAsynchronous();
void removeClockGroupsAsynchronous(const std::string &name);
bool sameClockGroup(const Clock *clk1,
const Clock *clk2) const;
// Clocks explicitly excluded by set_clock_group.
@ -756,7 +758,7 @@ public:
ExceptionThruSeq *thrus,
ExceptionTo *to,
const MinMaxAll *min_max);
void makeGroupPath(const char *name,
void makeGroupPath(const std::string &name,
bool is_default,
ExceptionFrom *from,
ExceptionThruSeq *thrus,
@ -1266,7 +1268,7 @@ protected:
void makeClkGroupExclusions(ClockGroupSet *groups);
void makeClkGroupSame(ClockGroup *group);
void clearClkGroupExclusions();
char *makeClockGroupsName();
std::string makeClockGroupsName();
void setClockSense(const Pin *pin,
const Clock *clk,
ClockSense sense);

View File

@ -274,7 +274,7 @@ protected:
const PatternMatch *pattern,
InstanceSeq &matches) const;
const char *staToSdc(const char *sta_name) const;
const char *staToSdc(std::string_view sta_name) const;
};
// Encapsulate a network to map names to/from the sdc namespace.

View File

@ -434,19 +434,21 @@ public:
const RiseFallBoth *to_rf,
const SetupHoldAll *setup_hold,
Sdc *sdc);
ClockGroups *makeClockGroups(const char *name,
ClockGroups *makeClockGroups(const std::string &name,
bool logically_exclusive,
bool physically_exclusive,
bool asynchronous,
bool allow_paths,
const char *comment,
Sdc *sdc);
// nullptr name removes all.
void removeClockGroupsLogicallyExclusive(const char *name,
void removeClockGroupsLogicallyExclusive(Sdc *sdc);
void removeClockGroupsLogicallyExclusive(const std::string &name,
Sdc *sdc);
void removeClockGroupsPhysicallyExclusive(const char *name,
void removeClockGroupsPhysicallyExclusive(Sdc *sdc);
void removeClockGroupsPhysicallyExclusive(const std::string &name,
Sdc *sdc);
void removeClockGroupsAsynchronous(const char *name,
void removeClockGroupsAsynchronous(Sdc *sdc);
void removeClockGroupsAsynchronous(const std::string &name,
Sdc *sdc);
void makeClockGroup(ClockGroups *clk_groups,
ClockSet *clks,
@ -640,7 +642,7 @@ public:
float delay,
const char *comment,
Sdc *sdc);
void makeGroupPath(const char *name,
void makeGroupPath(const std::string &name,
bool is_default,
ExceptionFrom *from,
ExceptionThruSeq *thrus,
@ -982,11 +984,11 @@ public:
bool report_cap,
bool report_slew,
bool report_fanout,
bool report_variation,
bool report_src_attr);
ReportField *findReportPathField(const char *name);
void setReportPathDigits(int digits);
void setReportPathNoSplit(bool no_split);
void setReportPathSigmas(bool report_sigmas);
void reportPathEnd(PathEnd *end);
void reportPathEnds(PathEndSeq *ends);
ReportPath *reportPath() { return report_path_; }
@ -998,7 +1000,7 @@ public:
const SetupHold *setup_hold,
bool include_internal_latency,
int digits);
float findWorstClkSkew(const SetupHold *setup_hold,
Delay findWorstClkSkew(const SetupHold *setup_hold,
bool include_internal_latency);
void reportClkLatency(ConstClockSeq &clks,
@ -1131,12 +1133,15 @@ public:
void reportArrivalWrtClks(const Pin *pin,
const Scene *scene,
bool report_variance,
int digits);
void reportRequiredWrtClks(const Pin *pin,
const Scene *scene,
bool report_variance,
int digits);
void reportSlackWrtClks(const Pin *pin,
const Scene *scene,
bool report_variance,
int digits);
Slew slew(Vertex *vertex,
@ -1144,9 +1149,9 @@ public:
const SceneSeq &scenes,
const MinMax *min_max);
ArcDelay arcDelay(Edge *edge,
TimingArc *arc,
DcalcAPIndex ap_index);
const ArcDelay arcDelay(Edge *edge,
TimingArc *arc,
DcalcAPIndex ap_index);
// True if the timing arc has been back-annotated.
bool arcDelayAnnotated(Edge *edge,
TimingArc *arc,
@ -1408,12 +1413,13 @@ public:
// TCL variable sta_crpr_mode.
CrprMode crprMode() const;
void setCrprMode(CrprMode mode);
// TCL variable sta_pocv_enabled.
// TCL variable sta_pocv_mode.
// Parametric on chip variation (statisical sta).
bool pocvEnabled() const;
void setPocvEnabled(bool enabled);
PocvMode pocvMode() const;
void setPocvMode(PocvMode mode);
// Number of std deviations from mean to use for normal distributions.
void setSigmaFactor(float factor);
float pocvQuantile();
void setPocvQuantile(float quantile);
// TCL variable sta_propagate_gated_clock_enable.
// Propagate gated clock enable arrivals.
bool propagateGatedClockEnable() const;
@ -1510,17 +1516,20 @@ protected:
void reportDelaysWrtClks(const Pin *pin,
const Scene *scene,
bool report_variance,
int digits,
bool find_required,
PathDelayFunc get_path_delay);
void reportDelaysWrtClks(Vertex *vertex,
const Scene *scene,
bool report_variance,
int digits,
bool find_required,
PathDelayFunc get_path_delay);
void reportDelaysWrtClks(Vertex *vertex,
const ClockEdge *clk_edge,
const Scene *scene,
bool report_variance,
int digits,
PathDelayFunc get_path_delay);
RiseFallMinMaxDelay findDelaysWrtClks(Vertex *vertex,
@ -1530,6 +1539,7 @@ protected:
std::string formatDelay(const RiseFall *rf,
const MinMax *min_max,
const RiseFallMinMaxDelay &delays,
bool report_variance,
int digits);
void connectDrvrPinAfter(Vertex *vertex);

View File

@ -47,6 +47,7 @@ class GraphDelayCalc;
class Latches;
class DispatchQueue;
class Variables;
class DelayOps;
using ModeSeq = std::vector<Mode*>;
using ModeSet = std::set<Mode*>;
@ -96,10 +97,10 @@ public:
GraphDelayCalc *graphDelayCalc() const { return graph_delay_calc_; }
Search *search() { return search_; }
Search *search() const { return search_; }
const DelayOps *delayOps() const { return delay_ops_; }
Latches *latches() { return latches_; }
Latches *latches() const { return latches_; }
unsigned threadCount() const { return thread_count_; }
float sigmaFactor() const { return sigma_factor_; }
bool crprActive(const Mode *mode) const;
Variables *variables() { return variables_; }
const Variables *variables() const { return variables_; }
@ -133,11 +134,11 @@ protected:
ArcDelayCalc *arc_delay_calc_;
GraphDelayCalc *graph_delay_calc_;
Search *search_;
DelayOps *delay_ops_;
Latches *latches_;
Variables *variables_;
int thread_count_;
DispatchQueue *dispatch_queue_;
float sigma_factor_;
};
} // namespace

View File

@ -143,14 +143,6 @@ public:
char *
stringCopy(const char *str);
inline void
stringAppend(char *&str1,
const char *str2)
{
strcpy(str1, str2);
str1 += strlen(str2);
}
void
stringDeleteCheck(const char *str);
@ -164,32 +156,6 @@ stringDelete(const char *str)
bool
isDigits(const char *str);
// Print to a new string.
// Caller owns returned string.
char *
stringPrint(const char *fmt,
...) __attribute__((format (printf, 1, 2)));
std::string
stdstrPrint(const char *fmt,
...) __attribute__((format (printf, 1, 2)));
char *
stringPrintArgs(const char *fmt,
va_list args);
void
stringPrint(std::string &str,
const char *fmt,
...) __attribute__((format (printf, 2, 3)));
// Formated append to std::string.
void
stringAppend(std::string &str,
const char *fmt,
...) __attribute__((format (printf, 2, 3)));
// Print to a temporary string.
char *
stringPrintTmp(const char *fmt,
...) __attribute__((format (printf, 1, 2)));
char *
makeTmpString(size_t length);
char *

View File

@ -33,12 +33,14 @@
#include "Transition.hh"
#include "LibertyClass.hh"
#include "TimingModel.hh"
#include "Variables.hh"
namespace sta {
class Unit;
class Units;
class Report;
class TableModels;
class Table;
class TableModel;
class TableAxis;
@ -63,43 +65,41 @@ class GateTableModel : public GateTimingModel
{
public:
GateTableModel(LibertyCell *cell,
TableModel *delay_model,
TableModelsEarlyLate delay_sigma_models,
TableModel *slew_model,
TableModelsEarlyLate slew_sigma_models,
TableModels *delay_models,
TableModels *slew_models,
ReceiverModelPtr receiver_model,
OutputWaveforms *output_waveforms);
GateTableModel(LibertyCell *cell,
TableModel *delay_model,
TableModel *slew_model);
TableModels *delay_models,
TableModels *slew_models);
~GateTableModel() override;
void gateDelay(const Pvt *pvt,
float in_slew,
float load_cap,
bool pocv_enabled,
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew) const override;
// deprecated 2024-01-07
// related_out_cap arg removed.
void gateDelay(const Pvt *pvt,
float in_slew,
float load_cap,
float related_out_cap,
bool pocv_enabled,
ArcDelay &gate_delay,
Slew &drvr_slew) const __attribute__ ((deprecated));
float &gate_delay,
float &drvr_slew) const override;
// Fill in pocv parameters in gate_delay, drvr_slew.
void gateDelayPocv(const Pvt *pvt,
float in_slew,
float load_cap,
const MinMax *min_max,
PocvMode pocv_mode,
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew) const override;
std::string reportGateDelay(const Pvt *pvt,
float in_slew,
float load_cap,
bool pocv_enabled,
const MinMax *min_max,
PocvMode pocv_mode,
int digits) const override;
float driveResistance(const Pvt *pvt) const override;
const TableModel *delayModel() const { return delay_model_.get(); }
const TableModel *slewModel() const { return slew_model_.get(); }
const TableModel *delaySigmaModel(const EarlyLate *el) const;
const TableModel *slewSigmaModel(const EarlyLate *el) const;
const TableModels *delayModels() const { return delay_models_.get(); }
const TableModel *delayModel() const;
const TableModels *slewModels() const { return slew_models_.get(); }
const TableModel *slewModel() const;
const ReceiverModel *receiverModel() const { return receiver_model_.get(); }
OutputWaveforms *outputWaveforms() const { return output_waveforms_.get(); }
// Check the axes before making the model.
@ -138,10 +138,8 @@ protected:
float &axis_value3) const;
static bool checkAxis(const TableAxis *axis);
std::unique_ptr<TableModel> delay_model_;
TableModelsEarlyLate delay_sigma_models_;
std::unique_ptr<TableModel> slew_model_;
TableModelsEarlyLate slew_sigma_models_;
std::unique_ptr<TableModels> delay_models_;
std::unique_ptr<TableModels> slew_models_;
ReceiverModelPtr receiver_model_;
std::unique_ptr<OutputWaveforms> output_waveforms_;
};
@ -150,25 +148,24 @@ class CheckTableModel : public CheckTimingModel
{
public:
CheckTableModel(LibertyCell *cell,
TableModel *model,
TableModelsEarlyLate sigma_models);
CheckTableModel(LibertyCell *cell,
TableModel *model);
TableModels *check_models);
~CheckTableModel() override;
ArcDelay checkDelay(const Pvt *pvt,
float from_slew,
float to_slew,
float related_out_cap,
bool pocv_enabled) const override;
const MinMax *min_max,
PocvMode pocv_mode) const override;
std::string reportCheckDelay(const Pvt *pvt,
float from_slew,
const char *from_slew_annotation,
float to_slew,
float related_out_cap,
bool pocv_enabled,
const MinMax *min_max,
PocvMode pocv_mode,
int digits) const override;
const TableModel *model() const { return model_.get(); }
const TableModel *sigmaModel(const EarlyLate *el) const;
const TableModels *checkModels() const { return check_models_.get(); }
const TableModel *checkModel() const;
// Check the axes before making the model.
// Return true if the model axes are supported.
@ -202,8 +199,7 @@ protected:
int digits) const;
static bool checkAxis(const TableAxis *axis);
std::unique_ptr<TableModel> model_;
TableModelsEarlyLate sigma_models_;
std::unique_ptr<TableModels> check_models_;
};
class TableAxis
@ -311,6 +307,8 @@ public:
private:
void clear();
float findValueOrder2(float axis_value1, float axis_value2) const;
float findValueOrder3(float axis_value1, float axis_value2, float axis_value3) const;
std::string reportValueOrder0(const char *result_name,
const char *comment1,
const Unit *table_unit,
@ -408,6 +406,34 @@ protected:
bool is_scaled_:1;
};
// cell/transition/check nldm/ocv/lvf models for one rise/fall edge.
class TableModels
{
public:
TableModels();
TableModels(TableModel *model);
~TableModels();
TableModel *model() const { return model_.get(); }
void setModel(TableModel *model);
TableModel *sigma(const EarlyLate *early_late) const;
void setSigma(TableModel *table,
const EarlyLate *early_late);
TableModel *meanShift() const { return mean_shift_.get(); }
void setMeanShift(TableModel *table);
TableModel *skewness() const { return skewness_.get(); }
void setSkewness(TableModel *table);
TableModel *stdDev() const { return std_dev_.get(); }
void setStdDev(TableModel *table);
protected:
std::unique_ptr<TableModel> model_;
// Note early/late can point to the same model.
std::array<TableModel*, EarlyLate::index_count> sigma_;
std::unique_ptr<TableModel> std_dev_;
std::unique_ptr<TableModel> mean_shift_;
std::unique_ptr<TableModel> skewness_;
};
////////////////////////////////////////////////////////////////
class ReceiverModel

View File

@ -28,6 +28,7 @@
#include "Delay.hh"
#include "LibertyClass.hh"
#include "Variables.hh"
namespace sta {
@ -52,14 +53,23 @@ public:
virtual void gateDelay(const Pvt *pvt,
float in_slew,
float load_cap,
bool pocv_enabled,
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew) const = 0;
float &gate_delay,
float &drvr_slew) const = 0;
// Fill in pocv parameters in gate_delay, drvr_slew.
virtual void gateDelayPocv(const Pvt *pvt,
float in_slew,
float load_cap,
const MinMax *min_max,
PocvMode pocv_mode,
// Return values.
ArcDelay &gate_delay,
Slew &drvr_slew) const = 0;
virtual std::string reportGateDelay(const Pvt *pvt,
float in_slew,
float load_cap,
bool pocv_enabled,
const MinMax *min_max,
PocvMode pocv_mode,
int digits) const = 0;
virtual float driveResistance(const Pvt *pvt) const = 0;
};
@ -74,13 +84,15 @@ public:
float from_slew,
float to_slew,
float related_out_cap,
bool pocv_enabled) const = 0;
const MinMax *min_max,
PocvMode pocv_mode) const = 0;
virtual std::string reportCheckDelay(const Pvt *pvt,
float from_slew,
const char *from_slew_annotation,
float to_slew,
float related_out_cap,
bool pocv_enabled,
const MinMax *min_max,
PocvMode pocv_mode,
int digits) const = 0;
};

View File

@ -56,9 +56,8 @@ public:
void setDigits(int digits);
// Does not include suffix.
int width() const;
const char *asString(float value) const;
const char *asString(double value) const;
const char *asString(float value,
std::string asString(float value) const;
std::string asString(float value,
int digits) const;
private:

View File

@ -24,6 +24,8 @@
#pragma once
#include "PocvMode.hh"
namespace sta {
enum class CrprMode { same_pin, same_transition };
@ -72,8 +74,11 @@ public:
// TCL variable sta_input_port_default_clock.
bool useDefaultArrivalClock() { return use_default_arrival_clock_; }
void setUseDefaultArrivalClock(bool enable);
bool pocvEnabled() const { return pocv_enabled_; }
void setPocvEnabled(bool enabled);
bool pocvEnabled() const;
PocvMode pocvMode() const { return pocv_mode_; }
void setPocvMode(PocvMode mode);
float pocvQuantile() const { return pocv_quantile_; }
void setPocvQuantile(float quartile);
private:
bool crpr_enabled_;
@ -88,7 +93,8 @@ private:
bool dynamic_loop_breaking_;
bool propagate_all_clks_;
bool use_default_arrival_clock_;
bool pocv_enabled_;
PocvMode pocv_mode_;
float pocv_quantile_;
};
} // namespace

View File

@ -29,21 +29,21 @@
namespace sta {
std::string
cellVerilogName(const char *sta_name);
cellVerilogName(std::string sta_name);
std::string
instanceVerilogName(const char *sta_name);
instanceVerilogName(std::string sta_name);
std::string
netVerilogName(const char *sta_name);
netVerilogName(std::string sta_name);
std::string
portVerilogName(const char *sta_name);
portVerilogName(std::string sta_name);
std::string
moduleVerilogToSta(const std::string *sta_name);
moduleVerilogToSta(std::string sta_name);
std::string
instanceVerilogToSta(const std::string *sta_name);
instanceVerilogToSta(std::string sta_name);
std::string
netVerilogToSta(const std::string *sta_name);
netVerilogToSta(std::string sta_name);
std::string
portVerilogToSta(const std::string *sta_name);
portVerilogToSta(std::string sta_name);
} // namespace

View File

@ -25,9 +25,12 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include <map>
#include "Format.hh"
#include "Report.hh"
#include "StringUtil.hh"
#include "NetworkClass.hh"
@ -59,8 +62,32 @@ class StringRegistry;
class VerilogBindingTbl;
class VerilogNetNameIterator;
class VerilogNetPortRef;
class VerilogError;
class LibertyCell;
class VerilogErrorCmp;
class VerilogError
{
public:
VerilogError(int id,
std::string_view filename,
int line,
std::string_view msg,
bool warn);
const char *msg() const { return msg_.c_str(); }
const char *filename() const { return filename_.c_str(); }
int id() const { return id_; }
int line() const { return line_; }
bool warn() const { return warn_; }
private:
int id_;
std::string filename_;
int line_;
std::string msg_;
bool warn_;
friend class VerilogErrorCmp;
};
using VerilogModuleMap = std::map<Cell*, VerilogModule*>;
using VerilogStmtSeq = std::vector<VerilogStmt*>;
@ -148,14 +175,24 @@ public:
const char *filename() const { return filename_.c_str(); }
void incrLine();
Report *report() const { return report_; }
template <typename... Args>
void error(int id,
const char *filename,
std::string_view filename,
int line,
const char *fmt, ...);
std::string_view fmt,
Args &&...args)
{
report_->fileError(id, filename, line, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
const char *filename,
std::string_view filename,
int line,
const char *fmt, ...);
std::string_view fmt,
Args &&...args)
{
report_->fileWarn(id, filename, line, fmt, std::forward<Args>(args)...);
}
const std::string &zeroNetName() const { return zero_net_name_; }
const std::string &oneNetName() const { return one_net_name_; }
void deleteModules();
@ -231,16 +268,26 @@ protected:
Instance *parent,
VerilogBindingTbl *parent_bindings,
bool is_leaf);
template <typename... Args>
void linkWarn(int id,
const char *filename,
std::string_view filename,
int line,
const char *msg, ...)
__attribute__((format (printf, 5, 6)));
std::string_view msg,
Args &&...args)
{
std::string msg_str = sta::formatRuntime(msg, std::forward<Args>(args)...);
link_errors_.push_back(new VerilogError(id, filename, line, msg_str, true));
}
template <typename... Args>
void linkError(int id,
const char *filename,
std::string_view filename,
int line,
const char *msg, ...)
__attribute__((format (printf, 5, 6)));
std::string_view msg,
Args &&...args)
{
std::string msg_str = sta::formatRuntime(msg, std::forward<Args>(args)...);
link_errors_.push_back(new VerilogError(id, filename, line, msg_str, false));
}
bool reportLinkErrors();
bool haveLinkErrors();
Cell *makeBlackBox(VerilogModuleInst *mod_inst,

View File

@ -37,7 +37,7 @@ InternalPower::InternalPower(LibertyPort *port,
LibertyPort *related_port,
LibertyPort *related_pg_pin,
const std::shared_ptr<FuncExpr> &when,
InternalPowerModels &models) :
const InternalPowerModels &models) :
port_(port),
related_port_(related_port),
related_pg_pin_(related_pg_pin),
@ -52,36 +52,32 @@ InternalPower::libertyCell() const
return port_->libertyCell();
}
const InternalPowerModel &
InternalPower::model(const RiseFall *rf) const
{
return models_[rf->index()];
}
float
InternalPower::power(const RiseFall *rf,
const Pvt *pvt,
float in_slew,
float load_cap) const
{
const std::shared_ptr<InternalPowerModel> &model = models_[rf->index()];
if (model)
return model->power(libertyCell(), pvt, in_slew, load_cap);
else
return 0.0;
}
const InternalPowerModel *
InternalPower::model(const RiseFall *rf) const
{
const std::shared_ptr<InternalPowerModel> &m = models_[rf->index()];
return m.get();
const InternalPowerModel &model = models_[rf->index()];
return model.power(libertyCell(), pvt, in_slew, load_cap);
}
////////////////////////////////////////////////////////////////
InternalPowerModel::InternalPowerModel(TableModel *model) :
model_(model)
InternalPowerModel::InternalPowerModel() :
model_(nullptr)
{
}
InternalPowerModel::~InternalPowerModel()
InternalPowerModel::InternalPowerModel(std::shared_ptr<TableModel> model) :
model_(model)
{
delete model_;
}
float

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "FuncExpr.hh"
@ -80,8 +80,7 @@ LibExprReader::makeFuncExprPort(const char *port_name)
if (port)
expr = FuncExpr::makePort(port);
else
report_->warn(1130, "%s references unknown port %s.",
error_msg_, port_name);
report_->warn(1130, "{} references unknown port {}.", error_msg_, port_name);
stringDelete(port_name);
return expr;
}
@ -134,7 +133,7 @@ LibExprReader::setResult(FuncExpr *result)
void
LibExprReader::parseError(const char *msg)
{
report_->error(1131, "%s %s.", error_msg_, msg);
report_->error(1131, "{} {}.", error_msg_, msg);
}
////////////////////////////////////////////////////////////////
@ -144,4 +143,4 @@ LibExprScanner::LibExprScanner(std::istringstream &stream) :
{
}
} // namespace
} // namespace sta

View File

@ -25,6 +25,7 @@
#include "Liberty.hh"
#include "ContainerHelpers.hh"
#include "Format.hh"
#include "Mutex.hh"
#include "EnumNameMap.hh"
#include "Report.hh"
@ -775,7 +776,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1,
port1->setScenePort(port2, ap_index);
}
else
report->warn(1110, "cell %s/%s port %s not found in cell %s/%s.",
report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.",
cell1->library()->name(),
cell1->name(),
port_name,
@ -801,7 +802,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1,
}
}
else
report->warn(1111, "cell %s/%s %s -> %s timing group %s not found in cell %s/%s.",
report->warn(1111, "cell {}/{} {} -> {} timing group {} not found in cell {}/{}.",
cell1->library()->name(),
cell1->name(),
arc_set1->from() ? arc_set1->from()->name() : "",
@ -820,7 +821,7 @@ LibertyLibrary::checkScenes(LibertyCell *cell,
for (const Scene *scene : scenes) {
for (auto min_max : MinMax::range()) {
if (!cell->checkSceneCell(scene, min_max))
report->error(1112, "Liberty cell %s/%s for corner %s/%s not found.",
report->error(1112, "Liberty cell {}/{} for corner {}/{} not found.",
cell->libertyLibrary()->name(),
cell->name(),
scene->name().c_str(),
@ -1262,7 +1263,7 @@ LibertyCell::makeInternalPower(LibertyPort *port,
LibertyPort *related_port,
LibertyPort *related_pg_pin,
const std::shared_ptr<FuncExpr> &when,
InternalPowerModels &models)
const InternalPowerModels &models)
{
internal_powers_.emplace_back(port, related_port, related_pg_pin, when, models);
port_internal_powers_[port].push_back(internal_powers_.size() - 1);
@ -1703,7 +1704,7 @@ LibertyCell::makeLatchEnables(Report *report,
TimingSense en_sense = en_func->portTimingSense(en);
if (en_sense == TimingSense::positive_unate
&& en_rf != RiseFall::rise())
report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.",
report->warn(1114, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function positive sense.",
library_->name(),
name(),
en->name(),
@ -1711,7 +1712,7 @@ LibertyCell::makeLatchEnables(Report *report,
en_rf == RiseFall::rise()?"rising":"falling");
else if (en_sense == TimingSense::negative_unate
&& en_rf != RiseFall::fall())
report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.",
report->warn(1115, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function negative sense.",
library_->name(),
name(),
en->name(),
@ -1721,7 +1722,7 @@ LibertyCell::makeLatchEnables(Report *report,
}
}
else
report->warn(1121, "cell %s/%s no latch enable found for %s -> %s.",
report->warn(1121, "cell {}/{} no latch enable found for {} -> {}.",
library_->name(),
name(),
d->name(),
@ -1767,7 +1768,7 @@ LibertyCell::findLatchSetup(const LibertyPort *d,
for (TimingArc *arc : arc_set->arcs()) {
const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
if (from_rf == en_rf) {
report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.",
report->warn(1113, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with {} -> {} setup_{} check.",
library_->name(),
name(),
en->name(),
@ -1824,7 +1825,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d,
latch_check_map_[setup_check] = idx;
d->setIsLatchData(true);
debugPrint(debug, "liberty_latch", 1,
"latch %s -> %s | %s %s -> %s | %s %s -> %s setup",
"latch {} -> {} | {} {} -> {} | {} {} -> {} setup",
d->name(),
q->name(),
en->name(),
@ -2904,7 +2905,7 @@ ModeDef::defineValue(const char *value,
const char *sdf_cond)
{
std::string key = value;
std::string sdf = sdf_cond ? std::string(sdf_cond) : std::string();
std::string sdf = sdf_cond ? sdf_cond : std::string();
auto [it, inserted] = values_.try_emplace(key, key, cond, std::move(sdf));
return &it->second;
}
@ -3041,7 +3042,7 @@ OperatingConditions::OperatingConditions(const char *name) :
Pvt(0.0, 0.0, 0.0),
name_(name),
// Default wireload tree.
wire_load_tree_(WireloadTree::balanced)
wire_load_tree_(WireloadTree::unknown)
{
}
@ -3202,27 +3203,27 @@ ScaleFactors::report(Report *report)
std::string line = " ";
for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
ScaleFactorPvt pvt = (ScaleFactorPvt) pvt_index;
stringAppend(line, "%10s", scaleFactorPvtName(pvt));
line += sta::format("{:>10}", scaleFactorPvtName(pvt));
}
report->reportLineString(line);
report->reportLine(line);
for (int type_index = 0; type_index < scale_factor_type_count; type_index++) {
ScaleFactorType type = (ScaleFactorType) type_index;
stringPrint(line, "%10s ", scaleFactorTypeName(type));
std::string line = sta::format("{:>10}", scaleFactorTypeName(type));
for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
if (scaleFactorTypeRiseFallSuffix(type)
|| scaleFactorTypeRiseFallPrefix(type)
|| scaleFactorTypeLowHighSuffix(type)) {
stringAppend(line, " %.3f,%.3f",
line += sta::format(" {:.3f},{:.3f}",
scales_[type_index][pvt_index][RiseFall::riseIndex()],
scales_[type_index][pvt_index][RiseFall::fallIndex()]);
}
else {
stringAppend(line, " %.3f",
line += sta::format(" {:.3f}",
scales_[type_index][pvt_index][0]);
}
}
report->reportLineString(line);
report->reportLine(line);
}
}

View File

@ -367,16 +367,13 @@ std::string to_string() { return self->to_string(); }
const TimingRole *role() { return self->role(); }
const char *sdf_cond() { return self->sdfCond().c_str(); }
const char *
std::string
full_name()
{
const char *from = self->from()->name();
const char *to = self->to()->name();
const char *cell_name = self->libertyCell()->name();
return stringPrintTmp("%s %s -> %s",
cell_name,
from,
to);
return sta::format("{} {} -> {}", cell_name, from, to);
}
const std::string

View File

@ -102,12 +102,8 @@ LibertyBuilder::makeBusPortBit(ConcreteLibrary *library,
const char *bus_name,
int bit_index)
{
std::string bit_name;
stringPrint(bit_name, "%s%c%d%c",
bus_name,
library->busBrktLeft(),
bit_index,
library->busBrktRight());
std::string bit_name = std::string(bus_name) + library->busBrktLeft()
+ std::to_string(bit_index) + library->busBrktRight();
LibertyPort *port = makePort(cell, bit_name.c_str(), bit_index);
bus_port->addPortBit(port);
cell->addPortBit(port);

View File

@ -45,7 +45,7 @@ sta::LibertyParse::error(const location_type &loc,
const std::string &msg)
{
reader->report()->fileError(164, reader->filename().c_str(),
loc.begin.line, "%s", msg.c_str());
loc.begin.line, "{}", msg);
}
%}
@ -169,13 +169,13 @@ attr_value:
/* Crafted to avoid conflicts with expr */
volt_expr:
FLOAT volt_op FLOAT
{ $$ = sta::stdstrPrint("%e%c%e", $1, $2, $3); }
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
| string volt_op FLOAT
{ $$ = sta::stdstrPrint("%s%c%e", $1.c_str(), $2, $3); }
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
| FLOAT volt_op string
{ $$ = sta::stdstrPrint("%e%c%s", $1, $2, $3.c_str()); }
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
| volt_expr volt_op FLOAT
{ $$ = sta::stdstrPrint("%s%c%e", $1.c_str(), $2, $3); }
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
;
volt_op:
@ -192,7 +192,7 @@ volt_op:
expr:
expr_term1
| expr_term1 expr_op expr
{ $$ = sta::stdstrPrint("%s%c%s", $1.c_str(), $2, $3.c_str()); }
{ $$ = sta::format("{}{}{}", $1.c_str(), $2, $3.c_str()); }
;
expr_term:

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "LibertyParser.hh"
@ -130,10 +130,8 @@ LibertyParser::groupBegin(const std::string type,
LibertyAttrValueSeq *params,
int line)
{
LibertyGroup *group =
new LibertyGroup(std::move(type),
params ? std::move(*params) : LibertyAttrValueSeq(),
line);
LibertyGroup *group = new LibertyGroup(
std::move(type), params ? std::move(*params) : LibertyAttrValueSeq(), line);
delete params;
LibertyGroup *parent_group = group_stack_.empty() ? nullptr : group_stack_.back();
group_visitor_->begin(group, parent_group);
@ -145,8 +143,7 @@ LibertyParser::groupEnd()
{
LibertyGroup *group = this->group();
group_stack_.pop_back();
LibertyGroup *parent =
group_stack_.empty() ? nullptr : group_stack_.back();
LibertyGroup *parent = group_stack_.empty() ? nullptr : group_stack_.back();
if (parent)
parent->addSubgroup(group);
group_visitor_->end(group, parent);
@ -170,8 +167,8 @@ LibertyParser::makeSimpleAttr(const std::string name,
const LibertyAttrValue *value,
int line)
{
LibertySimpleAttr *attr = new LibertySimpleAttr(std::move(name),
std::move(*value), line);
LibertySimpleAttr *attr =
new LibertySimpleAttr(std::move(name), std::move(*value), line);
delete value;
LibertyGroup *group = this->group();
group->addAttr(attr);
@ -191,9 +188,8 @@ LibertyParser::makeComplexAttr(const std::string name,
return nullptr; // Define is not a complex attr; already added to group
}
else {
LibertyComplexAttr *attr = new LibertyComplexAttr(std::move(name),
std::move(*values),
line);
LibertyComplexAttr *attr =
new LibertyComplexAttr(std::move(name), std::move(*values), line);
delete values;
LibertyGroup *group = this->group();
group->addAttr(attr);
@ -266,7 +262,7 @@ LibertyScanner::includeBegin()
}
else {
report_->fileWarn(25, filename_.c_str(), yylineno,
"cannot open include file %s.", filename.c_str());
"cannot open include file {}.", filename);
delete stream;
}
}
@ -291,7 +287,7 @@ LibertyScanner::fileEnd()
void
LibertyScanner::error(const char *msg)
{
report_->fileError(1866, filename_.c_str(), lineno(), "%s", msg);
report_->fileError(1866, filename_.c_str(), lineno(), "{}", msg);
}
////////////////////////////////////////////////////////////////
@ -305,10 +301,7 @@ LibertyGroup::LibertyGroup(std::string type,
{
}
LibertyGroup::~LibertyGroup()
{
clear();
}
LibertyGroup::~LibertyGroup() { clear(); }
void
LibertyGroup::clear()
@ -327,19 +320,15 @@ LibertyGroup::clear()
bool
LibertyGroup::empty() const
{
return subgroups_.empty()
&& simple_attr_map_.empty()
&& complex_attr_map_.empty()
&& define_map_.empty();
return subgroups_.empty() && simple_attr_map_.empty() && complex_attr_map_.empty()
&& define_map_.empty();
}
bool
LibertyGroup::oneGroupOnly() const
{
return subgroups_.size() == 1
&& simple_attr_map_.empty()
&& complex_attr_map_.empty()
&& define_map_.empty();
return subgroups_.size() == 1 && simple_attr_map_.empty()
&& complex_attr_map_.empty() && define_map_.empty();
}
void
@ -483,7 +472,7 @@ LibertyGroup::findAttrFloat(const std::string attr_name,
const std::string &float_str = attr_value.stringValue();
char *end = nullptr;
value = std::strtof(float_str.c_str(), &end);
if (end) {
if (end) {
exists = true;
return;
}
@ -538,10 +527,7 @@ LibertyComplexAttr::LibertyComplexAttr(std::string name,
{
}
LibertyComplexAttr::~LibertyComplexAttr()
{
deleteContents(values_);
}
LibertyComplexAttr::~LibertyComplexAttr() { deleteContents(values_); }
const LibertyAttrValue *
LibertyComplexAttr::firstValue() const
@ -585,9 +571,9 @@ LibertyAttrValue::floatValue() const
}
void
LibertyAttrValue::floatValue(// Return values.
float &value,
bool &valid) const
LibertyAttrValue::floatValue( // Return values.
float &value,
bool &valid) const
{
valid = false;
if (string_value_.empty()) {
@ -598,8 +584,7 @@ LibertyAttrValue::floatValue(// Return values.
// Some floats are enclosed in quotes.
char *end;
value = strtof(string_value_.c_str(), &end);
if ((*end == '\0'
|| isspace(*end))
if ((*end == '\0' || isspace(*end))
// strtof support INF as a valid float.
&& string_value_ != "inf") {
valid = true;
@ -631,4 +616,4 @@ LibertyVariable::LibertyVariable(std::string var,
{
}
} // namespace
} // namespace sta

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
#include <functional>
#include <memory>
#include <array>
#include <string_view>
#include <vector>
#include <unordered_map>
@ -44,6 +45,7 @@
#include "LibertyParser.hh"
#include "LibertyReader.hh"
#include "LibertyBuilder.hh"
#include "Report.hh"
namespace sta {
@ -232,25 +234,21 @@ protected:
const LibertyPortSeq &ports,
const LibertyGroup *port_group);
bool isGateTimingType(TimingType timing_type);
TableModel *readGateTableModel(const LibertyGroup *timing_group,
const char *table_group_name,
const RiseFall *rf,
TableTemplateType template_type,
float scale,
ScaleFactorType scale_factor_type);
TableModel *readTableModel(const LibertyGroup *timing_group,
const std::string &table_group_name,
const RiseFall *rf,
TableTemplateType template_type,
float scale,
ScaleFactorType scale_factor_type,
const std::function<bool(TableModel *model)> check_axes);
TableModelsEarlyLate
readEarlyLateTableModels(const LibertyGroup *timing_group,
const char *table_group_name,
const RiseFall *rf,
TableTemplateType template_type,
float scale,
ScaleFactorType scale_factor_type);
TableModel *readCheckTableModel(const LibertyGroup *timing_group,
const char *table_group_name,
const RiseFall *rf,
TableTemplateType template_type,
float scale,
ScaleFactorType scale_factor_type);
ScaleFactorType scale_factor_type,
const std::function<bool(TableModel *model)> check_axes);
ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group,
const RiseFall *rf);
void readReceiverCapacitance(const LibertyGroup *timing_group,
@ -268,7 +266,9 @@ protected:
const RiseFall *rf,
TableTemplateType template_type,
float scale,
ScaleFactorType scale_factor_type);
ScaleFactorType scale_factor_type,
const std::function<bool(TableModel *model)> &check_axes =
[](TableModel *) { return true; });
TablePtr readTableModel(const LibertyGroup *table_group,
const TableTemplate *tbl_template,
float scale);
@ -281,6 +281,14 @@ protected:
void makeTableModels(LibertyCell *cell,
const LibertyGroup *timing_group,
TimingArcAttrsPtr timing_attrs);
void readLvfModels(const LibertyGroup *timing_group,
const std::string &sigma_group_name,
const std::string &std_dev_group_name,
const std::string &mean_shift_group_name,
const std::string &skewness_group_name,
const RiseFall *rf,
TableModels *table_models,
const std::function<bool(TableModel *model)> check_axes);
TableAxisPtr makeTableAxis(const LibertyGroup *table_group,
const char *index_attr_name,
@ -445,38 +453,65 @@ protected:
const char *attr_name,
const LibertyCell *cell,
int line);
void libWarn(int id,
template <typename... Args>
void warn(int id,
const LibertyGroup *group,
const char *fmt,
...) const
__attribute__((format (printf, 4, 5)));
void libWarn(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileWarn(id, filename_, group->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
const LibertySimpleAttr *attr,
const char *fmt,
...) const
__attribute__((format (printf, 4, 5)));
void libWarn(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileWarn(id, filename_, attr->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
const LibertyComplexAttr *attr,
const char *fmt,
...) const
__attribute__((format (printf, 4, 5)));
void libWarn(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileWarn(id, filename_, attr->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
int line,
const char *fmt,
...) const
__attribute__((format (printf, 4, 5)));
void libError(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileWarn(id, filename_, line, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertyGroup *group,
const char *fmt, ...) const
__attribute__((format (printf, 4, 5)));
void libError(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileError(id, filename_, group->line(), fmt,
std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertySimpleAttr *attr,
const char *fmt, ...) const
__attribute__((format (printf, 4, 5)));
void libError(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileError(id, filename_, attr->line(), fmt,
std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertyComplexAttr *attr,
const char *fmt, ...) const
__attribute__((format (printf, 4, 5)));
std::string_view fmt,
Args &&...args) const
{
report_->fileError(id, filename_, attr->line(), fmt,
std::forward<Args>(args)...);
}
const char *filename_;
bool infer_latches_;

View File

@ -24,7 +24,9 @@
#pragma once
#include "LibertyLocation.hh"
#include <string>
#include <istream>
#include "LibertyParse.hh"
#ifndef __FLEX_LEXER_H

View File

@ -1,32 +1,34 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "LibertyWriter.hh"
#include <cstdlib>
#include <cmath>
#include <fstream>
#include "Format.hh"
#include "Units.hh"
#include "FuncExpr.hh"
#include "PortDirection.hh"
@ -44,7 +46,7 @@ class LibertyWriter
public:
LibertyWriter(const LibertyLibrary *lib,
const char *filename,
FILE *stream,
std::ofstream &stream,
Report *report);
void writeLibrary();
@ -80,7 +82,7 @@ protected:
const LibertyLibrary *library_;
const char *filename_;
FILE *stream_;
std::ofstream &stream_;
Report *report_;
const Unit *time_unit_;
const Unit *cap_unit_;
@ -91,11 +93,10 @@ writeLiberty(LibertyLibrary *lib,
const char *filename,
StaState *sta)
{
FILE *stream = fopen(filename, "w");
if (stream) {
std::ofstream stream(filename);
if (stream.is_open()) {
LibertyWriter writer(lib, filename, stream, sta->report());
writer.writeLibrary();
fclose(stream);
}
else
throw FileNotWritable(filename);
@ -103,7 +104,7 @@ writeLiberty(LibertyLibrary *lib,
LibertyWriter::LibertyWriter(const LibertyLibrary *lib,
const char *filename,
FILE *stream,
std::ofstream &stream,
Report *report) :
library_(lib),
filename_(filename),
@ -118,87 +119,87 @@ void
LibertyWriter::writeLibrary()
{
writeHeader();
fprintf(stream_, "\n");
sta::print(stream_, "\n");
writeTableTemplates();
writeBusDcls();
fprintf(stream_, "\n");
sta::print(stream_, "\n");
writeCells();
writeFooter();
}
void
LibertyWriter::writeHeader()
{
fprintf(stream_, "library (%s) {\n", library_->name());
fprintf(stream_, " comment : \"\";\n");
fprintf(stream_, " delay_model : table_lookup;\n");
fprintf(stream_, " simulation : false;\n");
sta::print(stream_, "library ({}) {{\n", library_->name());
sta::print(stream_, " comment : \"\";\n");
sta::print(stream_, " delay_model : table_lookup;\n");
sta::print(stream_, " simulation : false;\n");
const Unit *cap_unit = library_->units()->capacitanceUnit();
fprintf(stream_, " capacitive_load_unit (1,%s);\n",
cap_unit->scaleAbbrevSuffix().c_str());
fprintf(stream_, " leakage_power_unit : 1pW;\n");
sta::print(stream_, " capacitive_load_unit (1,{});\n",
cap_unit->scaleAbbrevSuffix());
sta::print(stream_, " leakage_power_unit : 1pW;\n");
const Unit *current_unit = library_->units()->currentUnit();
fprintf(stream_, " current_unit : \"1%s\";\n",
current_unit->scaleAbbrevSuffix().c_str());
sta::print(stream_, " current_unit : \"1{}\";\n",
current_unit->scaleAbbrevSuffix());
const Unit *res_unit = library_->units()->resistanceUnit();
fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n",
res_unit->scaleAbbrevSuffix().c_str());
sta::print(stream_, " pulling_resistance_unit : \"1{}\";\n",
res_unit->scaleAbbrevSuffix());
const Unit *time_unit = library_->units()->timeUnit();
fprintf(stream_, " time_unit : \"1%s\";\n",
time_unit->scaleAbbrevSuffix().c_str());
sta::print(stream_, " time_unit : \"1{}\";\n",
time_unit->scaleAbbrevSuffix());
const Unit *volt_unit = library_->units()->voltageUnit();
fprintf(stream_, " voltage_unit : \"1%s\";\n",
volt_unit->scaleAbbrevSuffix().c_str());
fprintf(stream_, " library_features(report_delay_calculation);\n");
fprintf(stream_, "\n");
sta::print(stream_, " voltage_unit : \"1{}\";\n",
volt_unit->scaleAbbrevSuffix());
sta::print(stream_, " library_features(report_delay_calculation);\n");
sta::print(stream_, "\n");
fprintf(stream_, " input_threshold_pct_rise : %.0f;\n",
library_->inputThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " input_threshold_pct_fall : %.0f;\n",
library_->inputThreshold(RiseFall::fall()) * 100);
fprintf(stream_, " output_threshold_pct_rise : %.0f;\n",
library_->inputThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " output_threshold_pct_fall : %.0f;\n",
library_->inputThreshold(RiseFall::fall()) * 100);
fprintf(stream_, " slew_lower_threshold_pct_rise : %.0f;\n",
library_->slewLowerThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " slew_lower_threshold_pct_fall : %.0f;\n",
library_->slewLowerThreshold(RiseFall::fall()) * 100);
fprintf(stream_, " slew_upper_threshold_pct_rise : %.0f;\n",
library_->slewUpperThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " slew_upper_threshold_pct_fall : %.0f;\n",
library_->slewUpperThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " slew_derate_from_library : %.1f;\n",
library_->slewDerateFromLibrary());
fprintf(stream_, "\n");
sta::print(stream_, " input_threshold_pct_rise : {:.0f};\n",
library_->inputThreshold(RiseFall::rise()) * 100);
sta::print(stream_, " input_threshold_pct_fall : {:.0f};\n",
library_->inputThreshold(RiseFall::fall()) * 100);
sta::print(stream_, " output_threshold_pct_rise : {:.0f};\n",
library_->inputThreshold(RiseFall::rise()) * 100);
sta::print(stream_, " output_threshold_pct_fall : {:.0f};\n",
library_->inputThreshold(RiseFall::fall()) * 100);
sta::print(stream_, " slew_lower_threshold_pct_rise : {:.0f};\n",
library_->slewLowerThreshold(RiseFall::rise()) * 100);
sta::print(stream_, " slew_lower_threshold_pct_fall : {:.0f};\n",
library_->slewLowerThreshold(RiseFall::fall()) * 100);
sta::print(stream_, " slew_upper_threshold_pct_rise : {:.0f};\n",
library_->slewUpperThreshold(RiseFall::rise()) * 100);
sta::print(stream_, " slew_upper_threshold_pct_fall : {:.0f};\n",
library_->slewUpperThreshold(RiseFall::rise()) * 100);
sta::print(stream_, " slew_derate_from_library : {:.1f};\n",
library_->slewDerateFromLibrary());
sta::print(stream_, "\n");
bool exists;
float max_fanout;
library_->defaultFanoutLoad(max_fanout, exists);
if (exists)
fprintf(stream_, " default_max_fanout : %.0f;\n", max_fanout);
sta::print(stream_, " default_max_fanout : {:.0f};\n", max_fanout);
float max_slew;
library_->defaultMaxSlew(max_slew, exists);
if (exists)
fprintf(stream_, " default_max_transition : %s;\n",
time_unit_->asString(max_slew, 3));
sta::print(stream_, " default_max_transition : {};\n",
time_unit_->asString(max_slew, 3));
float max_cap;
library_->defaultMaxCapacitance(max_cap, exists);
if (exists)
fprintf(stream_, " default_max_capacitance : %s;\n",
cap_unit_->asString(max_cap, 3));
sta::print(stream_, " default_max_capacitance : {};\n",
cap_unit_->asString(max_cap, 3));
float fanout_load;
library_->defaultFanoutLoad(fanout_load, exists);
if (exists)
fprintf(stream_, " default_fanout_load : %.2f;\n", fanout_load);
fprintf(stream_, "\n");
sta::print(stream_, " default_fanout_load : {:.2f};\n", fanout_load);
sta::print(stream_, "\n");
fprintf(stream_, " nom_process : %.1f;\n",
library_->nominalProcess());
fprintf(stream_, " nom_temperature : %.1f;\n",
library_->nominalTemperature());
fprintf(stream_, " nom_voltage : %.2f;\n",
library_->nominalVoltage());
sta::print(stream_, " nom_process : {:.1f};\n",
library_->nominalProcess());
sta::print(stream_, " nom_temperature : {:.1f};\n",
library_->nominalTemperature());
sta::print(stream_, " nom_voltage : {:.2f};\n",
library_->nominalVoltage());
}
void
@ -216,22 +217,22 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
const TableAxis *axis3 = tbl_template->axis3();
// skip scalar templates
if (axis1) {
fprintf(stream_, " lu_table_template(%s) {\n", tbl_template->name().c_str());
fprintf(stream_, " variable_1 : %s;\n",
tableVariableString(axis1->variable()));
sta::print(stream_, " lu_table_template({}) {{\n", tbl_template->name());
sta::print(stream_, " variable_1 : {};\n",
tableVariableString(axis1->variable()));
if (axis2)
fprintf(stream_, " variable_2 : %s;\n",
tableVariableString(axis2->variable()));
sta::print(stream_, " variable_2 : {};\n",
tableVariableString(axis2->variable()));
if (axis3)
fprintf(stream_, " variable_3 : %s;\n",
tableVariableString(axis3->variable()));
sta::print(stream_, " variable_3 : {};\n",
tableVariableString(axis3->variable()));
if (axis1 && !axis1->values().empty())
writeTableAxis4(axis1, 1);
if (axis2 && !axis2->values().empty())
writeTableAxis4(axis2, 2);
if (axis3 && !axis3->values().empty())
writeTableAxis4(axis3, 3);
fprintf(stream_, " }\n");
sta::print(stream_, " }}\n");
}
}
@ -240,16 +241,16 @@ void
LibertyWriter::writeTableAxis4(const TableAxis *axis,
int index)
{
fprintf(stream_, " index_%d(\"", index);
sta::print(stream_, " index_{}(\"", index);
const Unit *unit = tableVariableUnit(axis->variable(), library_->units());
bool first = true;
for (size_t i = 0; i < axis->size(); i++) {
if (!first)
fprintf(stream_, ", ");
fprintf(stream_, "%s", unit->asString(axis->axisValue(i), 5));
sta::print(stream_, ", ");
sta::print(stream_, "{}", unit->asString(axis->axisValue(i), 5));
first = false;
}
fprintf(stream_, "\");\n");
sta::print(stream_, "\");\n");
}
// indent 10
@ -257,7 +258,7 @@ void
LibertyWriter::writeTableAxis10(const TableAxis *axis,
int index)
{
fprintf(stream_, " ");
sta::print(stream_, " ");
writeTableAxis4(axis, index);
}
@ -266,13 +267,13 @@ LibertyWriter::writeBusDcls()
{
BusDclSeq dcls = library_->busDcls();
for (BusDcl *dcl : dcls) {
fprintf(stream_, " type (\"%s\") {\n", dcl->name().c_str());
fprintf(stream_, " base_type : array;\n");
fprintf(stream_, " data_type : bit;\n");
fprintf(stream_, " bit_width : %d;\n", std::abs(dcl->from() - dcl->to() + 1));
fprintf(stream_, " bit_from : %d;\n", dcl->from());
fprintf(stream_, " bit_to : %d;\n", dcl->to());
fprintf(stream_, " }\n");
sta::print(stream_, " type (\"{}\") {{\n", dcl->name());
sta::print(stream_, " base_type : array;\n");
sta::print(stream_, " data_type : bit;\n");
sta::print(stream_, " bit_width : {};\n", std::abs(dcl->from() - dcl->to() + 1));
sta::print(stream_, " bit_from : {};\n", dcl->from());
sta::print(stream_, " bit_to : {};\n", dcl->to());
sta::print(stream_, " }}\n");
}
}
@ -289,21 +290,20 @@ LibertyWriter::writeCells()
void
LibertyWriter::writeCell(const LibertyCell *cell)
{
fprintf(stream_, " cell (\"%s\") {\n", cell->name());
sta::print(stream_, " cell (\"{}\") {{\n", cell->name());
float area = cell->area();
if (area > 0.0)
fprintf(stream_, " area : %.3f \n", area);
sta::print(stream_, " area : {:.3f} \n", area);
if (cell->isMacro())
fprintf(stream_, " is_macro_cell : true;\n");
sta::print(stream_, " is_macro_cell : true;\n");
if (cell->interfaceTiming())
fprintf(stream_, " interface_timing : true;\n");
sta::print(stream_, " interface_timing : true;\n");
const char *footprint = cell->footprint();
if (footprint)
fprintf(stream_, " cell_footprint : \"%s\";\n", footprint);
sta::print(stream_, " cell_footprint : \"{}\";\n", footprint);
const char *user_function_class = cell->userFunctionClass();
if (user_function_class)
fprintf(stream_, " user_function_class : \"%s\";\n",
user_function_class);
sta::print(stream_, " user_function_class : \"{}\";\n", user_function_class);
LibertyCellPortIterator port_iter(cell);
while (port_iter.hasNext()) {
@ -314,24 +314,23 @@ LibertyWriter::writeCell(const LibertyCell *cell)
else if (port->isBus())
writeBusPort(port);
else if (port->isBundle())
report_->error(1340, "%s/%s bundled ports not supported.",
library_->name(),
report_->error(1340, "{}/{} bundled ports not supported.", library_->name(),
cell->name());
else
writePort(port);
}
}
fprintf(stream_, " }\n");
fprintf(stream_, "\n");
sta::print(stream_, " }}\n");
sta::print(stream_, "\n");
}
void
LibertyWriter::writeBusPort(const LibertyPort *port)
{
fprintf(stream_, " bus(\"%s\") {\n", port->name());
sta::print(stream_, " bus(\"{}\") {{\n", port->name());
if (port->busDcl())
fprintf(stream_, " bus_type : %s;\n", port->busDcl()->name().c_str());
sta::print(stream_, " bus_type : {};\n", port->busDcl()->name());
writePortAttrs(port);
LibertyPortMemberIterator member_iter(port);
@ -339,56 +338,53 @@ LibertyWriter::writeBusPort(const LibertyPort *port)
LibertyPort *member = member_iter.next();
writePort(member);
}
fprintf(stream_, " }\n");
sta::print(stream_, " }}\n");
}
void
LibertyWriter::writePort(const LibertyPort *port)
{
fprintf(stream_, " pin(\"%s\") {\n", port->name());
sta::print(stream_, " pin(\"{}\") {{\n", port->name());
writePortAttrs(port);
fprintf(stream_, " }\n");
sta::print(stream_, " }}\n");
}
void
LibertyWriter::writePortAttrs(const LibertyPort *port)
{
fprintf(stream_, " direction : %s;\n" , asString(port->direction()));
sta::print(stream_, " direction : {};\n", asString(port->direction()));
auto func = port->function();
if (func
// cannot ref internal ports until sequentials are written
&& !(func->port()
&& func->port()->direction()->isInternal()))
fprintf(stream_, " function : \"%s\";\n", func->to_string().c_str());
&& !(func->port() && func->port()->direction()->isInternal()))
sta::print(stream_, " function : \"{}\";\n", func->to_string());
auto tristate_enable = port->tristateEnable();
if (tristate_enable) {
if (tristate_enable->op() == FuncExpr::Op::not_) {
FuncExpr *three_state = tristate_enable->left();
fprintf(stream_, " three_state : \"%s\";\n",
three_state->to_string().c_str());
sta::print(stream_, " three_state : \"{}\";\n",
three_state->to_string());
}
else {
FuncExpr *three_state = tristate_enable->copy()->invert();
fprintf(stream_, " three_state : \"%s\";\n",
three_state->to_string().c_str());
sta::print(stream_, " three_state : \"{}\";\n",
three_state->to_string());
delete three_state;
}
}
if (port->isClock())
fprintf(stream_, " clock : true;\n");
fprintf(stream_, " capacitance : %s;\n",
cap_unit_->asString(port->capacitance(), 4));
sta::print(stream_, " clock : true;\n");
sta::print(stream_, " capacitance : {};\n",
cap_unit_->asString(port->capacitance(), 4));
float limit;
bool exists;
port->slewLimit(MinMax::max(), limit, exists);
if (exists)
fprintf(stream_, " max_transition : %s;\n",
time_unit_->asString(limit, 3));
sta::print(stream_, " max_transition : {};\n", time_unit_->asString(limit, 3));
port->capacitanceLimit(MinMax::max(), limit, exists);
if (exists)
fprintf(stream_, " max_capacitance : %s;\n",
cap_unit_->asString(limit, 3));
sta::print(stream_, " max_capacitance : {};\n", cap_unit_->asString(limit, 3));
for (TimingArcSet *arc_set : port->libertyCell()->timingArcSetsTo(port)) {
if (!isAutoWidthArc(port, arc_set))
@ -399,10 +395,10 @@ LibertyWriter::writePortAttrs(const LibertyPort *port)
void
LibertyWriter::writePwrGndPort(const LibertyPort *port)
{
fprintf(stream_, " pg_pin(\"%s\") {\n", port->name());
fprintf(stream_, " pg_type : \"%s\";\n", pwrGndTypeName(port->pwrGndType()));
fprintf(stream_, " voltage_name : \"%s\";\n", port->voltageName());
fprintf(stream_, " }\n");
sta::print(stream_, " pg_pin(\"{}\") {{\n", port->name());
sta::print(stream_, " pg_type : \"{}\";\n", pwrGndTypeName(port->pwrGndType()));
sta::print(stream_, " voltage_name : \"{}\";\n", port->voltageName());
sta::print(stream_, " }}\n");
}
// Check if arc is added for port min_pulse_width_high/low attribute.
@ -423,30 +419,27 @@ LibertyWriter::isAutoWidthArc(const LibertyPort *port,
void
LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
{
fprintf(stream_, " timing() {\n");
sta::print(stream_, " timing() {{\n");
if (arc_set->from())
fprintf(stream_, " related_pin : \"%s\";\n", arc_set->from()->name());
sta::print(stream_, " related_pin : \"{}\";\n", arc_set->from()->name());
TimingSense sense = arc_set->sense();
if (sense != TimingSense::unknown
&& sense != TimingSense::non_unate)
fprintf(stream_, " timing_sense : %s;\n",
to_string(sense));
if (sense != TimingSense::unknown && sense != TimingSense::non_unate)
sta::print(stream_, " timing_sense : {};\n", to_string(sense));
const char *timing_type = timingTypeString(arc_set);
if (timing_type)
fprintf(stream_, " timing_type : %s;\n", timing_type);
sta::print(stream_, " timing_type : {};\n", timing_type);
for (const RiseFall *rf : RiseFall::range()) {
TimingArc *arc = arc_set->arcTo(rf);
if (arc) {
// Min pulse width arcs are wrt to the leading edge of the pulse.
const RiseFall *model_rf = (arc_set->role() == TimingRole::width())
? rf->opposite()
: rf;
const RiseFall *model_rf =
(arc_set->role() == TimingRole::width()) ? rf->opposite() : rf;
writeTimingModels(arc, model_rf);
}
}
fprintf(stream_, " }\n");
sta::print(stream_, " }}\n");
}
void
@ -454,53 +447,53 @@ LibertyWriter::writeTimingModels(const TimingArc *arc,
const RiseFall *rf)
{
TimingModel *model = arc->model();
const GateTableModel *gate_model = dynamic_cast<GateTableModel*>(model);
const CheckTableModel *check_model = dynamic_cast<CheckTableModel*>(model);
const GateTableModel *gate_model = dynamic_cast<GateTableModel *>(model);
const CheckTableModel *check_model = dynamic_cast<CheckTableModel *>(model);
if (gate_model) {
const TableModel *delay_model = gate_model->delayModel();
const std::string &template_name = delay_model->tblTemplate()->name();
fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name.c_str());
sta::print(stream_, " cell_{}({}) {{\n", rf->name(), template_name);
writeTableModel(delay_model);
fprintf(stream_, " }\n");
sta::print(stream_, " }}\n");
const TableModel *slew_model = gate_model->slewModel();
if (slew_model) {
const std::string &slew_template_name = slew_model->tblTemplate()->name();
fprintf(stream_, " %s_transition(%s) {\n", rf->name(), slew_template_name.c_str());
sta::print(stream_, " {}_transition({}) {{\n", rf->name(),
slew_template_name);
writeTableModel(slew_model);
fprintf(stream_, " }\n");
sta::print(stream_, " }}\n");
}
}
else if (check_model) {
const TableModel *model = check_model->model();
const TableModel *model = check_model->checkModel();
const std::string &template_name = model->tblTemplate()->name();
fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name.c_str());
sta::print(stream_, " {}_constraint({}) {{\n", rf->name(),
template_name);
writeTableModel(model);
fprintf(stream_, " }\n");
sta::print(stream_, " }}\n");
}
else
report_->error(1341, "%s/%s/%s timing model not supported.",
library_->name(),
arc->from()->libertyCell()->name(),
arc->from()->name());
report_->error(1341, "{}/{}/{} timing model not supported.", library_->name(),
arc->from()->libertyCell()->name(), arc->from()->name());
}
void
LibertyWriter::writeTableModel(const TableModel *model)
{
switch (model->order()) {
case 0:
writeTableModel0(model);
break;
case 1:
writeTableModel1(model);
break;
case 2:
writeTableModel2(model);
break;
case 3:
report_->error(1342, "3 axis table models not supported.");
break;
case 0:
writeTableModel0(model);
break;
case 1:
writeTableModel1(model);
break;
case 2:
writeTableModel2(model);
break;
case 3:
report_->error(1342, "3 axis table models not supported.");
break;
}
}
@ -508,24 +501,23 @@ void
LibertyWriter::writeTableModel0(const TableModel *model)
{
float value = model->value(0, 0, 0);
fprintf(stream_, " values(\"%s\");\n",
time_unit_->asString(value, 5));
sta::print(stream_, " values(\"{}\");\n", time_unit_->asString(value, 5));
}
void
LibertyWriter::writeTableModel1(const TableModel *model)
{
writeTableAxis10(model->axis1(), 1);
fprintf(stream_, " values(\"");
sta::print(stream_, " values(\"");
bool first_col = true;
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
float value = model->value(index1, 0, 0);
if (!first_col)
fprintf(stream_, ",");
fprintf(stream_, "%s", time_unit_->asString(value, 5));
sta::print(stream_, ",");
sta::print(stream_, "{}", time_unit_->asString(value, 5));
first_col = false;
}
fprintf(stream_, "\");\n");
sta::print(stream_, "\");\n");
}
void
@ -533,31 +525,31 @@ LibertyWriter::writeTableModel2(const TableModel *model)
{
writeTableAxis10(model->axis1(), 1);
writeTableAxis10(model->axis2(), 2);
fprintf(stream_, " values(\"");
sta::print(stream_, " values(\"");
bool first_row = true;
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
if (!first_row) {
fprintf(stream_, "\\\n");
fprintf(stream_, " \"");
sta::print(stream_, "\\\n");
sta::print(stream_, " \"");
}
bool first_col = true;
for (size_t index2 = 0; index2 < model->axis2()->size(); index2++) {
float value = model->value(index1, index2, 0);
if (!first_col)
fprintf(stream_, ",");
fprintf(stream_, "%s", time_unit_->asString(value, 5));
sta::print(stream_, ",");
sta::print(stream_, "{}", time_unit_->asString(value, 5));
first_col = false;
}
fprintf(stream_, "\"");
sta::print(stream_, "\"");
first_row = false;
}
fprintf(stream_, ");\n");
sta::print(stream_, ");\n");
}
void
LibertyWriter::writeFooter()
{
fprintf(stream_, "}\n");
sta::print(stream_, "}}\n");
}
const char *
@ -571,15 +563,13 @@ LibertyWriter::asString(const PortDirection *dir)
{
if (dir == PortDirection::input())
return "input";
else if (dir == PortDirection::output()
|| (dir == PortDirection::tristate()))
else if (dir == PortDirection::output() || (dir == PortDirection::tristate()))
return "output";
else if (dir == PortDirection::internal())
return "internal";
else if (dir == PortDirection::bidirect())
return "inout";
else if (dir == PortDirection::ground()
|| dir == PortDirection::power())
else if (dir == PortDirection::ground() || dir == PortDirection::power())
return "input";
return "unknown";
}
@ -594,8 +584,7 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
return "three_state_disable";
else if (role == TimingRole::tristateEnable())
return "three_state_enable";
else if (role == TimingRole::regClkToQ()
|| role == TimingRole::latchEnToQ()) {
else if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) {
const TimingArc *arc = arc_set->arcs()[0];
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
return "rising_edge";
@ -611,16 +600,14 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
else
return "clear";
}
else if (role == TimingRole::setup()
|| role == TimingRole::recovery()) {
else if (role == TimingRole::setup() || role == TimingRole::recovery()) {
const TimingArc *arc = arc_set->arcs()[0];
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
return "setup_rising";
else
return "setup_falling";
}
else if (role == TimingRole::hold()
|| role == TimingRole::removal()) {
else if (role == TimingRole::hold() || role == TimingRole::removal()) {
const TimingArc *arc = arc_set->arcs()[0];
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
return "hold_rising";
@ -648,13 +635,11 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
else if (role == TimingRole::width())
return "min_pulse_width";
else {
report_->error(1343, "%s/%s/%s timing arc type %s not supported.",
library_->name(),
arc_set->to()->libertyCell()->name(),
arc_set->to()->name(),
role->to_string().c_str());
report_->error(1343, "{}/{}/{} timing arc type {} not supported.",
library_->name(), arc_set->to()->libertyCell()->name(),
arc_set->to()->name(), role->to_string());
return nullptr;
}
}
} // namespace
} // namespace sta

View File

@ -42,20 +42,32 @@ void
GateLinearModel::gateDelay(const Pvt *,
float,
float load_cap,
bool,
// return values
ArcDelay &gate_delay,
Slew &drvr_slew) const
float &gate_delay,
float &drvr_slew) const
{
gate_delay = intrinsic_ + resistance_ * load_cap;
drvr_slew = 0.0;
}
void
GateLinearModel::gateDelayPocv(const Pvt *,
float,
float,
const MinMax *,
PocvMode,
// return values
ArcDelay &,
Slew &) const
{
}
std::string
GateLinearModel::reportGateDelay(const Pvt *,
float,
float load_cap,
bool,
const MinMax *,
PocvMode,
int digits) const
{
const LibertyLibrary *library = cell_->libertyLibrary();
@ -98,7 +110,8 @@ CheckLinearModel::checkDelay(const Pvt *,
float,
float,
float,
bool) const
const MinMax *,
PocvMode) const
{
return intrinsic_;
}
@ -109,7 +122,8 @@ CheckLinearModel::reportCheckDelay(const Pvt *,
const char *,
float,
float,
bool,
const MinMax *,
PocvMode,
int digits) const
{
const LibertyLibrary *library = cell_->libertyLibrary();

File diff suppressed because it is too large Load Diff

View File

@ -152,9 +152,8 @@ TimingArc::intrinsicDelay() const
{
GateTimingModel *model = dynamic_cast<GateTimingModel*>(model_);
if (model) {
ArcDelay arc_delay;
Slew slew;
model->gateDelay(nullptr, 0.0, 0.0, false, arc_delay, slew);
float arc_delay, slew;
model->gateDelay(nullptr, 0.0, 0.0, arc_delay, slew);
return arc_delay;
}
else

View File

@ -26,6 +26,7 @@
#include <cmath> // abs
#include "Format.hh"
#include "StringUtil.hh"
#include "MinMax.hh" // INF
#include "Fuzzy.hh"
@ -127,7 +128,7 @@ Unit::scaleString() const
else if (fuzzyEqual(scale_, 1E-15))
return "1f";
else
return stdstrPrint("%.1e", scale_);
return sta::format("{:.1e}", scale_);
}
std::string
@ -155,19 +156,13 @@ Unit::width() const
return digits_ + 2;
}
const char *
std::string
Unit::asString(float value) const
{
return asString(value, digits_);
}
const char *
Unit::asString(double value) const
{
return asString(static_cast<float>(value), digits_);
}
const char *
std::string
Unit::asString(float value,
int digits) const
{
@ -179,7 +174,7 @@ Unit::asString(float value,
// prevent "-0.00" on slowaris
if (std::abs(scaled_value) < 1E-6)
scaled_value = 0.0;
return stringPrintTmp("%.*f", digits, scaled_value);
return sta::formatRuntime("{:.{}f}", scaled_value, digits);
}
}

View File

@ -60,8 +60,8 @@ TEST_F(UnitTest, UserToSta) {
TEST_F(UnitTest, AsString) {
Unit unit(1e-9f, "s", 3);
const char *str = unit.asString(1e-9f);
EXPECT_NE(str, nullptr);
std::string str = unit.asString(1e-9f);
EXPECT_FALSE(str.empty());
// Should produce something like "1.000"
}
@ -1268,8 +1268,8 @@ TEST(InternalPowerTest, DirectConstruction) {
InternalPower pwr(nullptr, nullptr, nullptr, when_expr, models);
EXPECT_EQ(pwr.when(), when_expr.get());
EXPECT_EQ(pwr.relatedPgPin(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::rise()), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr);
EXPECT_EQ(pwr.model(RiseFall::rise()).model(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()).model(), nullptr);
}
////////////////////////////////////////////////////////////////
@ -1450,26 +1450,27 @@ TEST_F(LinearModelTest, GateLinearModelConstruct) {
TEST_F(LinearModelTest, GateLinearModelGateDelay) {
GateLinearModel model(cell_, 1.0f, 2.0f);
ArcDelay gate_delay;
Slew drvr_slew;
float gate_delay;
float drvr_slew;
// delay = intrinsic + resistance * load_cap = 1.0 + 2.0 * 3.0 = 7.0
model.gateDelay(nullptr, 0.0f, 3.0f, false, gate_delay, drvr_slew);
EXPECT_FLOAT_EQ(delayAsFloat(gate_delay), 7.0f);
EXPECT_FLOAT_EQ(delayAsFloat(drvr_slew), 0.0f);
model.gateDelay(nullptr, 0.0f, 3.0f, gate_delay, drvr_slew);
EXPECT_FLOAT_EQ(gate_delay, 7.0f);
EXPECT_FLOAT_EQ(drvr_slew, 0.0f);
}
TEST_F(LinearModelTest, GateLinearModelZeroLoad) {
GateLinearModel model(cell_, 2.5f, 1.0f);
ArcDelay gate_delay;
Slew drvr_slew;
float gate_delay;
float drvr_slew;
// delay = 2.5 + 1.0 * 0.0 = 2.5
model.gateDelay(nullptr, 0.0f, 0.0f, false, gate_delay, drvr_slew);
EXPECT_FLOAT_EQ(delayAsFloat(gate_delay), 2.5f);
model.gateDelay(nullptr, 0.0f, 0.0f, gate_delay, drvr_slew);
EXPECT_FLOAT_EQ(gate_delay, 2.5f);
}
TEST_F(LinearModelTest, GateLinearModelReportGateDelay) {
GateLinearModel model(cell_, 1.0f, 2.0f);
std::string report = model.reportGateDelay(nullptr, 0.0f, 0.5f, false, 3);
std::string report = model.reportGateDelay(nullptr, 0.0f, 0.5f,
nullptr, PocvMode::scalar, 3);
EXPECT_FALSE(report.empty());
// Report should contain "Delay ="
EXPECT_NE(report.find("Delay"), std::string::npos);
@ -1477,23 +1478,27 @@ TEST_F(LinearModelTest, GateLinearModelReportGateDelay) {
TEST_F(LinearModelTest, CheckLinearModelConstruct) {
CheckLinearModel model(cell_, 3.0f);
ArcDelay delay = model.checkDelay(nullptr, 0.0f, 0.0f, 0.0f, false);
ArcDelay delay = model.checkDelay(nullptr, 0.0f, 0.0f, 0.0f,
nullptr, PocvMode::scalar);
EXPECT_FLOAT_EQ(delayAsFloat(delay), 3.0f);
}
TEST_F(LinearModelTest, CheckLinearModelCheckDelay) {
CheckLinearModel model(cell_, 5.5f);
// checkDelay always returns intrinsic_ regardless of other params
ArcDelay delay1 = model.checkDelay(nullptr, 1.0f, 2.0f, 3.0f, true);
ArcDelay delay1 = model.checkDelay(nullptr, 1.0f, 2.0f, 3.0f,
nullptr, PocvMode::scalar);
EXPECT_FLOAT_EQ(delayAsFloat(delay1), 5.5f);
ArcDelay delay2 = model.checkDelay(nullptr, 0.0f, 0.0f, 0.0f, false);
ArcDelay delay2 = model.checkDelay(nullptr, 0.0f, 0.0f, 0.0f,
nullptr, PocvMode::scalar);
EXPECT_FLOAT_EQ(delayAsFloat(delay2), 5.5f);
}
TEST_F(LinearModelTest, CheckLinearModelReportCheckDelay) {
CheckLinearModel model(cell_, 2.0f);
std::string report = model.reportCheckDelay(nullptr, 0.0f, nullptr,
0.0f, 0.0f, false, 3);
0.0f, 0.0f,
nullptr, PocvMode::scalar, 3);
EXPECT_FALSE(report.empty());
EXPECT_NE(report.find("Check"), std::string::npos);
}
@ -1505,24 +1510,24 @@ TEST_F(LinearModelTest, CheckLinearModelReportCheckDelay) {
TEST(InternalPowerTest, ModelAccess) {
InternalPowerModels models{};
InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models);
// Initially models should be nullptr
EXPECT_EQ(pwr.model(RiseFall::rise()), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr);
// Initially models should have null inner TableModel
EXPECT_EQ(pwr.model(RiseFall::rise()).model(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()).model(), nullptr);
}
TEST(InternalPowerTest, WithModel) {
// Create a minimal model: Table -> TableModel -> InternalPowerModel
TablePtr tbl = std::make_shared<Table>(1.0f);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
auto power_model = std::make_shared<InternalPowerModel>(table_model);
auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel power_model(table_model);
InternalPowerModels models{};
models[RiseFall::riseIndex()] = power_model;
InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models);
EXPECT_EQ(pwr.model(RiseFall::rise()), power_model.get());
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr);
EXPECT_NE(pwr.model(RiseFall::rise()).model(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()).model(), nullptr);
}
////////////////////////////////////////////////////////////////
@ -3473,9 +3478,9 @@ TEST(LibertyUtilTest, PortLibertyToStaWithBrackets) {
TEST(InternalPowerModelTest, PowerLookupOrder0) {
TablePtr tbl = std::make_shared<Table>(5.0f);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel model(table_model);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
@ -3485,9 +3490,9 @@ TEST(InternalPowerModelTest, PowerLookupOrder0) {
TEST(InternalPowerModelTest, ReportPowerOrder0) {
TablePtr tbl = std::make_shared<Table>(3.0f);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel model(table_model);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
@ -3505,9 +3510,9 @@ TEST(InternalPowerModelTest, PowerLookupOrder1) {
values.push_back(1.0f);
values.push_back(3.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel model(table_model);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");
@ -3529,9 +3534,9 @@ TEST(InternalPowerModelTest, PowerLookupOrder2) {
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel *table_model = new TableModel(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power,
RiseFall::rise());
InternalPowerModel model(table_model);
LibertyLibrary lib("test_lib", "test.lib");
TestCell cell(&lib, "INV", "test.lib");

View File

@ -222,12 +222,10 @@ ConcreteCell::makeBusPortBit(ConcretePort *bus_port,
const char *bus_name,
int bit_index)
{
std::string bit_name;
stringPrint(bit_name, "%s%c%d%c",
bus_name,
library_->busBrktLeft(),
bit_index,
library_->busBrktRight());
std::string bit_name = std::string(bus_name)
+ library_->busBrktLeft()
+ std::to_string(bit_index)
+ library_->busBrktRight();
ConcretePort *port = makePort(bit_name.c_str(), bit_index);
bus_port->addPortBit(port);
addPortBit(port);
@ -465,12 +463,13 @@ ConcretePort::busName() const
{
if (is_bus_) {
ConcreteLibrary *lib = cell_->library();
return stringPrintTmp("%s%c%d:%d%c",
name(),
lib->busBrktLeft(),
from_index_,
to_index_,
lib->busBrktRight());
std::string bus_name = sta::format("{}{}{}:{}{}",
name(),
lib->busBrktLeft(),
from_index_,
to_index_,
lib->busBrktRight());
return makeTmpString(bus_name);
}
else
return name();

View File

@ -2003,7 +2003,7 @@ ConcreteNetwork::linkNetwork(const char *top_cell_name,
return top_instance_ != nullptr;
}
else {
report->error(1000, "cell type %s can not be linked.", top_cell_name);
report->error(1000, "cell type {} can not be linked.", top_cell_name);
return false;
}
}

View File

@ -293,15 +293,16 @@ HpinDrvrLoad::~HpinDrvrLoad()
void
HpinDrvrLoad::report(const Network *network)
{
printf("%s -> %s: ",
drvr_ ? network->pathName(drvr_) : "-",
load_ ? network->pathName(load_) : "-");
Report *report = network->report();
std::string line = sta::format("{} -> {}: ",
drvr_ ? network->pathName(drvr_) : "-",
load_ ? network->pathName(load_) : "-");
for (const Pin *pin : *hpins_from_drvr_)
printf("%s ", network->pathName(pin));
printf("* ");
line += sta::format("{} ", network->pathName(pin));
line += "* ";
for (const Pin *pin : *hpins_to_load_)
printf("%s ", network->pathName(pin));
printf("\n");
line += sta::format("{} ", network->pathName(pin));
report->report(line);
}
void

View File

@ -262,24 +262,15 @@ Network::pathName(const Instance *instance) const
{
InstanceSeq inst_path;
path(instance, inst_path);
size_t name_length = 0;
for (const Instance *inst : inst_path)
name_length += strlen(name(inst)) + 1;
char *path_name = makeTmpString(name_length + 1);
char *path_ptr = path_name;
// Top instance has null string name, so terminate the string here.
*path_name = '\0';
std::string path_name;
while (inst_path.size()) {
const Instance *inst = inst_path.back();
const char *inst_name = name(inst);
strcpy(path_ptr, inst_name);
path_ptr += strlen(inst_name);
path_name += name(inst);
inst_path.pop_back();
if (inst_path.size())
*path_ptr++ = pathDivider();
*path_ptr = '\0';
if (!inst_path.empty())
path_name += pathDivider();
}
return path_name;
return makeTmpString(path_name);
}
bool
@ -376,18 +367,10 @@ Network::pathName(const Pin *pin) const
{
const Instance *inst = instance(pin);
if (inst && inst != topInstance()) {
const char *inst_name = pathName(inst);
size_t inst_name_length = strlen(inst_name);
const char *port_name = portName(pin);
size_t port_name_length = strlen(port_name);
size_t path_name_length = inst_name_length + port_name_length + 2;
char *path_name = makeTmpString(path_name_length);
char *path_ptr = path_name;
strcpy(path_ptr, inst_name);
path_ptr += inst_name_length;
*path_ptr++ = pathDivider();
strcpy(path_ptr, port_name);
return path_name;
std::string path_name = pathName(inst);
path_name += pathDivider();
path_name += portName(pin);
return makeTmpString(path_name);
}
else
return portName(pin);
@ -464,18 +447,10 @@ Network::pathName(const Net *net) const
{
const Instance *inst = instance(net);
if (inst && inst != topInstance()) {
const char *inst_name = pathName(inst);
size_t inst_name_length = strlen(inst_name);
const char *net_name = name(net);
size_t net_name_length = strlen(net_name);
size_t path_name_length = inst_name_length + net_name_length + 2;
char *path_name = makeTmpString(path_name_length);
char *path_ptr = path_name;
strcpy(path_ptr, inst_name);
path_ptr += inst_name_length;
*path_ptr++ = pathDivider();
strcpy(path_ptr, net_name);
return path_name;
std::string path_name = pathName(inst);
path_name += pathDivider();
path_name += name(net);
return makeTmpString(path_name);
}
else
return name(net);

View File

@ -511,7 +511,7 @@ net_pins(Net *net)
return pins;
}
const char *
std::string
pin_location(const Pin *pin)
{
Network *network = Sta::sta()->ensureLinked();
@ -520,12 +520,12 @@ pin_location(const Pin *pin)
network->location(pin, x, y, exists);
// return x/y as tcl list
if (exists)
return sta::stringPrintTmp("%f %f", x, y);
return sta::format("{} {}", x, y);
else
return "";
}
const char *
std::string
port_location(const Port *port)
{
Network *network = Sta::sta()->ensureLinked();

View File

@ -24,35 +24,34 @@
#include "ParseBus.hh"
#include <cstring>
#include <cstdlib>
#include <string>
#include <string_view>
#include "StringUtil.hh"
namespace sta {
bool
isBusName(const char *name,
isBusName(std::string_view name,
const char brkt_left,
const char brkt_right,
char escape)
{
size_t len = strlen(name);
size_t len = name.size();
// Shortest bus name is a[0].
if (len >= 4
// Escaped bus brackets are not buses.
&& name[len - 2] != escape
&& name[len - 1] == brkt_right) {
const char *left = strrchr(name, brkt_left);
return left != nullptr;
size_t left = name.rfind(brkt_left);
return left != std::string_view::npos;
}
else
return false;
}
void
parseBusName(const char *name,
parseBusName(std::string_view name,
const char brkt_left,
const char brkt_right,
const char escape,
@ -61,16 +60,15 @@ parseBusName(const char *name,
std::string &bus_name,
int &index)
{
const char brkts_left[2] = {brkt_left, '\0'};
const char brkts_right[2] = {brkt_right, '\0'};
parseBusName(name, brkts_left, brkts_right, escape,
parseBusName(name, std::string_view(&brkt_left, 1),
std::string_view(&brkt_right, 1), escape,
is_bus, bus_name, index);
}
void
parseBusName(const char *name,
const char *brkts_left,
const char *brkts_right,
parseBusName(std::string_view name,
std::string_view brkts_left,
std::string_view brkts_right,
char escape,
// Return values.
bool &is_bus,
@ -78,30 +76,28 @@ parseBusName(const char *name,
int &index)
{
is_bus = false;
size_t len = strlen(name);
size_t len = name.size();
// Shortest bus name is a[0].
if (len >= 4
// Escaped bus brackets are not buses.
&& name[len - 2] != escape) {
char last_ch = name[len - 1];
const char *brkt_right_ptr = strchr(brkts_right, last_ch);
if (brkt_right_ptr) {
size_t brkt_index = brkt_right_ptr - brkts_right;
char brkt_left = brkts_left[brkt_index];
const char *left = strrchr(name, brkt_left);
if (left) {
size_t brkt_index = brkts_right.find(last_ch);
if (brkt_index != std::string_view::npos) {
char brkt_left_ch = brkts_left[brkt_index];
size_t left = name.rfind(brkt_left_ch);
if (left != std::string_view::npos) {
is_bus = true;
size_t bus_name_len = left - name;
bus_name.append(name, bus_name_len);
bus_name.append(name.data(), left);
// Simple bus subscript.
index = atoi(left + 1);
index = std::stoi(std::string(name.substr(left + 1)));
}
}
}
}
void
parseBusName(const char *name,
parseBusName(std::string_view name,
const char brkt_left,
const char brkt_right,
char escape,
@ -113,16 +109,15 @@ parseBusName(const char *name,
int &to,
bool &subscript_wild)
{
const char brkts_left[2] = {brkt_left, '\0'};
const char brkts_right[2] = {brkt_right, '\0'};
parseBusName(name, brkts_left, brkts_right, escape,
parseBusName(name, std::string_view(&brkt_left, 1),
std::string_view(&brkt_right, 1), escape,
is_bus, is_range, bus_name, from, to, subscript_wild);
}
void
parseBusName(const char *name,
const char *brkts_left,
const char *brkts_right,
parseBusName(std::string_view name,
std::string_view brkts_left,
std::string_view brkts_right,
char escape,
// Return values.
bool &is_bus,
@ -135,36 +130,31 @@ parseBusName(const char *name,
is_bus = false;
is_range = false;
subscript_wild = false;
size_t len = strlen(name);
size_t len = name.size();
// Shortest bus is a[0].
if (len >= 4
// Escaped bus brackets are not buses.
&& name[len - 2] != escape) {
char last_ch = name[len - 1];
const char *brkt_right_ptr = strchr(brkts_right, last_ch);
if (brkt_right_ptr) {
size_t brkt_index = brkt_right_ptr - brkts_right;
char brkt_left = brkts_left[brkt_index];
const char *left = strrchr(name, brkt_left);
if (left) {
size_t brkt_index = brkts_right.find(last_ch);
if (brkt_index != std::string_view::npos) {
char brkt_left_ch = brkts_left[brkt_index];
size_t left = name.rfind(brkt_left_ch);
if (left != std::string_view::npos) {
is_bus = true;
bus_name.append(name.data(), left);
// Check for bus range.
const char range_sep = ':';
const char *range = strchr(name, range_sep);
if (range) {
size_t range = name.find(':', left);
if (range != std::string_view::npos) {
is_range = true;
bus_name.append(name, left - name);
// No need to terminate bus subscript because atoi stops
// scanning at first non-digit character.
from = atoi(left + 1);
to = atoi(range + 1);
from = std::stoi(std::string(name.substr(left + 1)));
to = std::stoi(std::string(name.substr(range + 1)));
}
else {
bus_name.append(name, left - name);
if (left[1] == '*')
if (left + 1 < len && name[left + 1] == '*')
subscript_wild = true;
else
from = to = atoi(left + 1);
from = to = std::stoi(std::string(name.substr(left + 1)));
}
}
}
@ -172,22 +162,23 @@ parseBusName(const char *name,
}
std::string
escapeChars(const char *token,
escapeChars(std::string_view token,
const char ch1,
const char ch2,
const char escape)
{
std::string escaped;
for (const char *s = token; *s; s++) {
char ch = *s;
escaped.reserve(token.size());
for (size_t i = 0; i < token.size(); i++) {
char ch = token[i];
if (ch == escape) {
char next_ch = s[1];
// Make sure we don't skip the null if escape is the last char.
if (next_ch != '\0') {
if (i + 1 < token.size()) {
escaped += ch;
escaped += next_ch;
s++;
escaped += token[i + 1];
i++;
}
else
escaped += ch;
}
else if (ch == ch1 || ch == ch2) {
escaped += escape;

View File

@ -640,28 +640,27 @@ SdcNetwork::SdcNetwork(Network *network) :
// Translate sta namespace to sdc namespace.
// Remove all escapes.
const char *
SdcNetwork::staToSdc(const char *sta_name) const
SdcNetwork::staToSdc(std::string_view sta_name) const
{
char escape = pathEscape();
char *sdc_name = makeTmpString(strlen(sta_name) + 1);
char *d = sdc_name;
for (const char *s = sta_name; *s; s++) {
char ch = s[0];
size_t sta_length = sta_name.length();
std::string sdc_name;
for (size_t i = 0; i < sta_length; i++) {
char ch = sta_name[i];
if (ch == escape) {
char next_ch = s[1];
char next_ch = sta_name[i + 1];
// Escaped escape.
if (next_ch == escape) {
*d++ = ch;
*d++ = next_ch;
s++;
sdc_name += ch;
sdc_name += next_ch;
i++;
}
}
else
// Non escape.
*d++ = ch;
sdc_name += ch;
}
*d++ = '\0';
return sdc_name;
return makeTmpString(sdc_name);
}
Port *
@ -680,11 +679,11 @@ SdcNetwork::findPort(const Cell *cell,
port = network_->findPort(cell, escaped1.c_str());
if (port == nullptr) {
// Try escaping base foo\[0\][1]
std::string escaped2;
std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this);
stringPrint(escaped2, "%s[%d]",
escaped_bus_name.c_str(),
index);
std::string escaped2 = escaped_bus_name
+ '['
+ std::to_string(index)
+ ']';
port = network_->findPort(cell, escaped2.c_str());
}
}
@ -966,8 +965,10 @@ SdcNetwork::findPin(const Instance *instance,
if (pin == nullptr) {
// Try escaping base foo\[0\][1]
std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this);
std::string escaped2;
stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index);
std::string escaped2 = escaped_bus_name
+ '['
+ std::to_string(index)
+ ']';
pin = network_->findPin(instance, escaped2.c_str());
}
}
@ -1149,46 +1150,39 @@ SdcNetwork::parsePath(const char *path,
const char *&path_tail) const
{
Instance *parent = topInstance();
std::string inst_path;
// Leave room to escape all the dividers and '\0'.
int inst_path_length = path_length + divider_count + 1;
char *inst_path = new char[inst_path_length];
inst_path.reserve(path_length + divider_count + 1);
inst = nullptr;
path_tail = path;
char *p = inst_path;
for (const char *s = path; *s; s++) {
char ch = *s;
if (ch == escape_) {
// Make sure we don't skip the null if escape is the last char.
if (s[1] != '\0') {
*p++ = ch;
*p++ = s[1];
inst_path += ch;
inst_path += s[1];
s++;
}
}
else if (ch == divider_) {
// Terminate the sub-path up to this divider.
*p = '\0';
Instance *child = findChild(parent, inst_path);
Instance *child = findChild(parent, inst_path.c_str());
if (child) {
// Found an instance for the sub-path up to this divider.
parent = inst = child;
// Reset the instance path.
p = inst_path;
inst_path.clear();
path_tail = s + 1;
}
else {
// No match for sub-path. Escape the divider and keep looking.
*p++ = escape_;
*p++ = divider_;
inst_path += escape_;
inst_path += divider_;
}
}
else
*p++ = ch;
if (p - inst_path + 1 > inst_path_length)
report_->critical(1500, "inst path std::string lenth estimate busted");
inst_path += ch;
}
*p = '\0';
stringDelete(inst_path);
}
// Helper to visit instance path matches.
@ -1207,11 +1201,9 @@ SdcNetwork::visitMatches(const Instance *parent,
{
int divider_count, path_length;
scanPath(pattern->pattern(), divider_count, path_length);
std::string inst_path;
// Leave room to escape all the dividers and '\0'.
int inst_path_length = path_length + divider_count + 1;
char *inst_path = new char[inst_path_length];
char *p = inst_path;
inst_path.reserve(path_length + divider_count + 1);
bool has_brkts = false;
bool found_match = false;
for (const char *s = pattern->pattern(); *s; s++) {
@ -1219,20 +1211,18 @@ SdcNetwork::visitMatches(const Instance *parent,
if (ch == escape_) {
// Make sure we don't skip the null if escape is the last char.
if (s[1] != '\0') {
*p++ = ch;
*p++ = s[1];
inst_path += ch;
inst_path += s[1];
s++;
}
}
else if (ch == divider_) {
// Terminate the sub-path up to this divider.
*p = '\0';
PatternMatch matcher(inst_path, pattern);
PatternMatch matcher(inst_path.c_str(), pattern);
InstanceSeq matches;
network_->findChildrenMatching(parent, &matcher, matches);
if (has_brkts && matches.empty()) {
// Look for matches after escaping brackets.
std::string escaped_brkts = escapeBrackets(inst_path, this);
std::string escaped_brkts = escapeBrackets(inst_path.c_str(), this);
const PatternMatch escaped_pattern(escaped_brkts, pattern);
network_->findChildrenMatching(parent, &escaped_pattern, matches);
}
@ -1245,29 +1235,25 @@ SdcNetwork::visitMatches(const Instance *parent,
found_match |= visitMatches(match, &tail_pattern, visit_tail);
}
// Escape the divider and keep looking.
*p++ = escape_;
*p++ = divider_;
inst_path += escape_;
inst_path += divider_;
}
else {
if (ch == '[' || ch == ']')
has_brkts = true;
*p++ = ch;
inst_path += ch;
}
if (p - inst_path + 1 > inst_path_length)
report_->critical(1501, "inst path std::string lenth estimate exceeded");
}
*p = '\0';
if (!found_match) {
PatternMatch tail_pattern(inst_path, pattern);
PatternMatch tail_pattern(inst_path.c_str(), pattern);
found_match |= visit_tail(parent, &tail_pattern);
if (!found_match && has_brkts) {
// Look for matches after escaping brackets.
std::string escaped_path = escapeBrackets(inst_path, this);
std::string escaped_path = escapeBrackets(inst_path.c_str(), this);
const PatternMatch escaped_tail(escaped_path, pattern);
found_match |= visit_tail(parent, &escaped_tail);
}
}
stringDelete(inst_path);
return found_match;
}

View File

@ -34,35 +34,34 @@ namespace sta {
constexpr char verilog_escape = '\\';
static std::string
staToVerilog(const char *sta_name);
staToVerilog(std::string sta_name);
static std::string
staToVerilog2(const char *sta_name);
staToVerilog2(std::string sta_name);
static std::string
verilogToSta(const std::string *verilog_name);
verilogToSta(const std::string verilog_name);
std::string
cellVerilogName(const char *sta_name)
cellVerilogName(std::string sta_name)
{
return staToVerilog(sta_name);
}
std::string
instanceVerilogName(const char *sta_name)
instanceVerilogName(std::string sta_name)
{
return staToVerilog(sta_name);
}
std::string
netVerilogName(const char *sta_name)
netVerilogName(std::string sta_name)
{
bool is_bus;
std::string bus_name;
int index;
parseBusName(sta_name, '[', ']', verilog_escape, is_bus, bus_name, index);
parseBusName(sta_name.c_str(), '[', ']', verilog_escape, is_bus, bus_name, index);
if (is_bus) {
std::string bus_vname = staToVerilog(bus_name.c_str());
std::string vname;
stringPrint(vname, "%s[%d]", bus_vname.c_str(), index);
std::string vname = bus_vname + '[' + std::to_string(index) + ']';
return vname;
}
else
@ -70,27 +69,28 @@ netVerilogName(const char *sta_name)
}
std::string
portVerilogName(const char *sta_name)
portVerilogName(std::string sta_name)
{
return staToVerilog2(sta_name);
}
static std::string
staToVerilog(const char *sta_name)
staToVerilog(std::string sta_name)
{
// Leave room for leading escape and trailing space if the name
// needs to be escaped.
// Assume the name has to be escaped and start copying while scanning.
std::string escaped_name = "\\";
bool escaped = false;
for (const char *s = sta_name; *s ; s++) {
char ch = s[0];
size_t sta_length = sta_name.size();
for (size_t i = 0; i < sta_length; i++) {
char ch = sta_name[i];
if (ch == verilog_escape) {
escaped = true;
char next_ch = s[1];
char next_ch = sta_name[i + 1];
if (next_ch == verilog_escape) {
escaped_name += next_ch;
s++;
i++;
}
}
else {
@ -105,11 +105,11 @@ staToVerilog(const char *sta_name)
return escaped_name;
}
else
return std::string(sta_name);
return sta_name;
}
static std::string
staToVerilog2(const char *sta_name)
staToVerilog2(std::string sta_name)
{
constexpr char bus_brkt_left = '[';
constexpr char bus_brkt_right = ']';
@ -118,14 +118,15 @@ staToVerilog2(const char *sta_name)
std::string escaped_name = "\\";
// Assume the name has to be escaped and start copying while scanning.
bool escaped = false;
for (const char *s = sta_name; *s ; s++) {
char ch = s[0];
size_t sta_length = sta_name.size();
for (size_t i = 0; i < sta_length; i++) {
char ch = sta_name[i];
if (ch == verilog_escape) {
escaped = true;
char next_ch = s[1];
char next_ch = sta_name[i + 1];
if (next_ch == verilog_escape) {
escaped_name += next_ch;
s++;
i++;
}
}
else {
@ -142,50 +143,50 @@ staToVerilog2(const char *sta_name)
return escaped_name;
}
else
return std::string(sta_name);
return sta_name;
}
////////////////////////////////////////////////////////////////
std::string
moduleVerilogToSta(const std::string *module_name)
moduleVerilogToSta(std::string module_name)
{
return verilogToSta(module_name);
}
std::string
instanceVerilogToSta(const std::string *inst_name)
instanceVerilogToSta(std::string inst_name)
{
return verilogToSta(inst_name);
}
std::string
netVerilogToSta(const std::string *net_name)
netVerilogToSta(std::string net_name)
{
return verilogToSta(net_name);
}
std::string
portVerilogToSta(const std::string *port_name)
portVerilogToSta(std::string port_name)
{
return verilogToSta(port_name);
}
static std::string
verilogToSta(const std::string *verilog_name)
verilogToSta(std::string verilog_name)
{
if (verilog_name->front() == '\\') {
if (verilog_name.front() == '\\') {
constexpr char divider = '/';
constexpr char bus_brkt_left = '[';
constexpr char bus_brkt_right = ']';
size_t verilog_name_length = verilog_name->size();
if (isspace(verilog_name->back()))
size_t verilog_name_length = verilog_name.size();
if (isspace(verilog_name.back()))
verilog_name_length--;
std::string sta_name;
// Ignore leading '\'.
for (size_t i = 1; i < verilog_name_length; i++) {
char ch = verilog_name->at(i);
char ch = verilog_name[i];
if (ch == bus_brkt_left
|| ch == bus_brkt_right
|| ch == divider
@ -197,7 +198,7 @@ verilogToSta(const std::string *verilog_name)
return sta_name;
}
else
return std::string(*verilog_name);
return verilog_name;
}
} // namespace

Some files were not shown because too many files have changed in this diff Show More