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) cmake_policy(SET CMP0086 NEW)
endif() endif()
project(STA VERSION 3.0.0 project(STA VERSION 3.0.1
LANGUAGES CXX LANGUAGES CXX
) )
@ -89,13 +89,17 @@ endif()
set(STA_SOURCE set(STA_SOURCE
app/StaMain.cc app/StaMain.cc
dcalc/ArcDelayCalc.cc
dcalc/ArcDcalcWaveforms.cc dcalc/ArcDcalcWaveforms.cc
dcalc/ArcDelayCalc.cc
dcalc/ArnoldiDelayCalc.cc dcalc/ArnoldiDelayCalc.cc
dcalc/ArnoldiReduce.cc dcalc/ArnoldiReduce.cc
dcalc/CcsCeffDelayCalc.cc dcalc/CcsCeffDelayCalc.cc
dcalc/Delay.cc
dcalc/DelayCalc.cc dcalc/DelayCalc.cc
dcalc/DelayCalcBase.cc dcalc/DelayCalcBase.cc
dcalc/DelayNormal.cc
dcalc/DelayScalar.cc
dcalc/DelaySkewNormal.cc
dcalc/DmpCeff.cc dcalc/DmpCeff.cc
dcalc/DmpDelayCalc.cc dcalc/DmpDelayCalc.cc
dcalc/FindRoot.cc dcalc/FindRoot.cc
@ -106,9 +110,6 @@ set(STA_SOURCE
dcalc/PrimaDelayCalc.cc dcalc/PrimaDelayCalc.cc
dcalc/UnitDelayCalc.cc dcalc/UnitDelayCalc.cc
graph/DelayFloat.cc
graph/DelayNormal1.cc
graph/DelayNormal2.cc
graph/Graph.cc graph/Graph.cc
graph/GraphCmp.cc graph/GraphCmp.cc
@ -206,6 +207,7 @@ set(STA_SOURCE
search/PathEnum.cc search/PathEnum.cc
search/PathExpanded.cc search/PathExpanded.cc
search/PathGroup.cc search/PathGroup.cc
search/PocvMode.cc
search/Property.cc search/Property.cc
search/ReportPath.cc search/ReportPath.cc
search/Search.cc search/Search.cc
@ -408,12 +410,47 @@ find_package(Threads)
find_package(Eigen3 REQUIRED) find_package(Eigen3 REQUIRED)
include(cmake/FindCUDD.cmake) # See if std::format is available and if nor install fmt.
include(CheckCXXSourceCompiles)
if("${SSTA}" STREQUAL "") set(_sta_fmt_check_saved_flags "${CMAKE_REQUIRED_FLAGS}")
set(SSTA 0) if(MSVC)
string(APPEND CMAKE_REQUIRED_FLAGS " /std:c++20")
else()
string(APPEND CMAKE_REQUIRED_FLAGS " -std=c++20")
endif() 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 a header file to pass some of the CMake settings
configure_file(${STA_HOME}/util/StaConfig.hh.cmake configure_file(${STA_HOME}/util/StaConfig.hh.cmake
@ -533,6 +570,10 @@ target_link_libraries(OpenSTA
${CUDD_LIB} ${CUDD_LIB}
) )
if(NOT HAVE_CXX_STD_FORMAT)
target_link_libraries(OpenSTA fmt::fmt)
endif()
if (ZLIB_LIBRARIES) if (ZLIB_LIBRARIES)
target_link_libraries(OpenSTA ${ZLIB_LIBRARIES}) target_link_libraries(OpenSTA ${ZLIB_LIBRARIES})
endif() endif()
@ -561,7 +602,7 @@ endif()
# common to gcc/clang # common to gcc/clang
set(CXX_FLAGS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls set(CXX_FLAGS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls
-Wformat-security -Werror=misleading-indentation) -Wformat-security -Werror=misleading-indentation -Wundef)
if(ENABLE_TSAN) if(ENABLE_TSAN)
message(STATUS "Thread sanitizer: ${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 rm -rf v1.14.0.tar.gz googletest-1.14.0
EOF 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 FROM base-dependencies AS builder
COPY . /OpenSTA COPY . /OpenSTA

View File

@ -13,13 +13,14 @@ RUN apt-get update && \
gdb \ gdb \
tcl-dev \ tcl-dev \
tcl-tclreadline \ tcl-tclreadline \
libeigen3-dev \
swig \ swig \
bison \ bison \
flex \ flex \
automake \ automake \
autotools-dev \ autotools-dev \
libgtest-dev libgtest-dev \
libeigen3-dev \
libfmt-dev
# Download CUDD # Download CUDD
RUN wget https://raw.githubusercontent.com/davidkebo/cudd/main/cudd_versions/cudd-3.0.0.tar.gz && \ 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 cudd 3.0.0 3.0.0 BSD required
tclreadline 2.3.8 2.3.8 BSD optional tclreadline 2.3.8 2.3.8 BSD optional
zLib 1.2.5 1.2.8 zlib 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) 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 You can use the "configure --prefix" option and "make install" to install CUDD
in a different directory. 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 ### Building with CMake
Use the following commands to checkout the git repository and build the Use the following commands to checkout the git repository and build the

View File

@ -96,8 +96,7 @@ sourceTclFile(const char *filename,
bool verbose, bool verbose,
Tcl_Interp *interp) Tcl_Interp *interp)
{ {
std::string cmd; std::string cmd = sta::format("sta::include_file {} {} {}",
stringPrint(cmd, "sta::include_file %s %s %s",
filename, filename,
echo ? "1" : "0", echo ? "1" : "0",
verbose ? "1" : "0"); verbose ? "1" : "0");

View File

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

View File

@ -94,24 +94,24 @@ makeArcDcalcArg(const char *inst_name,
else { else {
const Network *network = sta->network(); const Network *network = sta->network();
const Instance *inst = network->instance(in_pin); 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)); network->pathName(inst));
} }
} }
else 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 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 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 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 else
report->warn(2105, "Instance %s not found.", inst_name); report->warn(2105, "Instance {} not found.", inst_name);
return ArcDcalcArg(); return ArcDcalcArg();
} }
@ -257,18 +257,18 @@ ArcDcalcResult::ArcDcalcResult(size_t load_count) :
} }
void void
ArcDcalcResult::setGateDelay(ArcDelay gate_delay) ArcDcalcResult::setGateDelay(const ArcDelay &gate_delay)
{ {
gate_delay_ = gate_delay; gate_delay_ = gate_delay;
} }
void void
ArcDcalcResult::setDrvrSlew(Slew drvr_slew) ArcDcalcResult::setDrvrSlew(const Slew &drvr_slew)
{ {
drvr_slew_ = drvr_slew; drvr_slew_ = drvr_slew;
} }
ArcDelay const ArcDelay &
ArcDcalcResult::wireDelay(size_t load_idx) const ArcDcalcResult::wireDelay(size_t load_idx) const
{ {
return wire_delays_[load_idx]; return wire_delays_[load_idx];
@ -276,7 +276,7 @@ ArcDcalcResult::wireDelay(size_t load_idx) const
void void
ArcDcalcResult::setWireDelay(size_t load_idx, ArcDcalcResult::setWireDelay(size_t load_idx,
ArcDelay wire_delay) const ArcDelay &wire_delay)
{ {
wire_delays_[load_idx] = wire_delay; wire_delays_[load_idx] = wire_delay;
} }
@ -288,7 +288,7 @@ ArcDcalcResult::setLoadCount(size_t load_count)
load_slews_.resize(load_count); load_slews_.resize(load_count);
} }
Slew const Slew &
ArcDcalcResult::loadSlew(size_t load_idx) const ArcDcalcResult::loadSlew(size_t load_idx) const
{ {
return load_slews_[load_idx]; return load_slews_[load_idx];
@ -296,7 +296,7 @@ ArcDcalcResult::loadSlew(size_t load_idx) const
void void
ArcDcalcResult::setLoadSlew(size_t load_idx, ArcDcalcResult::setLoadSlew(size_t load_idx,
Slew load_slew) const Slew &load_slew)
{ {
load_slews_[load_idx] = load_slew; load_slews_[load_idx] = load_slew;
} }

View File

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

View File

@ -34,6 +34,7 @@
#include "Network.hh" #include "Network.hh"
#include "Units.hh" #include "Units.hh"
#include "Arnoldi.hh" #include "Arnoldi.hh"
#include "Format.hh"
#include "parasitics/ConcreteParasiticsPvt.hh" #include "parasitics/ConcreteParasiticsPvt.hh"
namespace sta { namespace sta {
@ -43,10 +44,7 @@ rcmodel::rcmodel() :
{ {
} }
rcmodel::~rcmodel() rcmodel::~rcmodel() { free(pinV); }
{
free(pinV);
}
float float
rcmodel::capacitance() const rcmodel::capacitance() const
@ -85,7 +83,6 @@ struct ts_edge
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
const int ArnoldiReduce::ts_point_count_incr_ = 1024; const int ArnoldiReduce::ts_point_count_incr_ = 1024;
const int ArnoldiReduce::ts_edge_count_incr_ = 1024; const int ArnoldiReduce::ts_edge_count_incr_ = 1024;
@ -96,31 +93,32 @@ ArnoldiReduce::ArnoldiReduce(StaState *sta) :
termNmax(256), termNmax(256),
dNmax(8) dNmax(8)
{ {
ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point)); ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point));
ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int)); ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int));
ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*)); ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *));
_u0 = (double*)malloc(ts_pointNmax*sizeof(double)); _u0 = (double *)malloc(ts_pointNmax * sizeof(double));
_u1 = (double*)malloc(ts_pointNmax*sizeof(double)); _u1 = (double *)malloc(ts_pointNmax * sizeof(double));
y = (double*)malloc(ts_pointNmax*sizeof(double)); y = (double *)malloc(ts_pointNmax * sizeof(double));
iv = (double*)malloc(ts_pointNmax*sizeof(double)); iv = (double *)malloc(ts_pointNmax * sizeof(double));
r = (double*)malloc(ts_pointNmax*sizeof(double)); r = (double *)malloc(ts_pointNmax * sizeof(double));
c = (double*)malloc(ts_pointNmax*sizeof(double)); c = (double *)malloc(ts_pointNmax * sizeof(double));
par = (int*)malloc(ts_pointNmax*sizeof(int)); par = (int *)malloc(ts_pointNmax * sizeof(int));
ts_edgeV = (ts_edge*)malloc(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_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *));
ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*)); ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *));
pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*)); pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *));
termV = (int*)malloc(termNmax*sizeof(int)); termV = (int *)malloc(termNmax * sizeof(int));
outV = (int*)malloc(termNmax*sizeof(int)); outV = (int *)malloc(termNmax * sizeof(int));
d = (double*)malloc(dNmax*sizeof(double)); d = (double *)malloc(dNmax * sizeof(double));
e = (double*)malloc(dNmax*sizeof(double)); e = (double *)malloc(dNmax * sizeof(double));
U = (double**)malloc(dNmax*sizeof(double*)); U = (double **)malloc(dNmax * sizeof(double *));
U0 = (double*)malloc(dNmax*termNmax*sizeof(double)); U0 = (double *)malloc(dNmax * termNmax * sizeof(double));
int h; 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() ArnoldiReduce::~ArnoldiReduce()
@ -161,7 +159,7 @@ ArnoldiReduce::reduceToArnoldi(Parasitic *parasitic,
scene_ = scene; scene_ = scene;
min_max_ = min_max; min_max_ = min_max;
parasitics_ = scene->parasitics(min_max); parasitics_ = scene->parasitics(min_max);
parasitic_network_ = reinterpret_cast<ConcreteParasiticNetwork*>(parasitic); parasitic_network_ = reinterpret_cast<ConcreteParasiticNetwork *>(parasitic);
loadWork(); loadWork();
return makeRcmodelDrv(); return makeRcmodelDrv();
@ -200,7 +198,7 @@ ArnoldiReduce::loadWork()
ts_edge *e; ts_edge *e;
int tindex; int tindex;
for (p = p0; p!=pend; p++) { for (p = p0; p != pend; p++) {
p->node_ = nullptr; p->node_ = nullptr;
p->eN = 0; p->eN = 0;
p->is_term = false; p->is_term = false;
@ -246,14 +244,14 @@ ArnoldiReduce::loadWork()
e++; e++;
} }
for (p=p0;p!=pend;p++) { for (p = p0; p != pend; p++) {
if (p->node_) { if (p->node_) {
p->eV = eV; p->eV = eV;
eV += p->eN; eV += p->eN;
p->eN = 0; p->eN = 0;
} }
} }
for (e=e0;e!=eend;e++) { for (e = e0; e != eend; e++) {
e->from->eV[e->from->eN++] = e; e->from->eV[e->from->eN++] = e;
if (e->to != e->from) if (e->to != e->from)
e->to->eV[e->to->eN++] = e; e->to->eV[e->to->eN++] = e;
@ -267,30 +265,33 @@ ArnoldiReduce::allocPoints()
free(par); free(par);
free(c); free(c);
free(r); free(r);
free(iv); free(y); free(_u1); free(_u0); free(iv);
free(y);
free(_u1);
free(_u0);
free(ts_pordV); free(ts_pordV);
free(ts_ordV); free(ts_ordV);
free(ts_pointV); free(ts_pointV);
ts_pointNmax = ts_pointN + ts_point_count_incr_; ts_pointNmax = ts_pointN + ts_point_count_incr_;
ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point)); ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point));
ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int)); ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int));
ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*)); ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *));
_u0 = (double*)malloc(ts_pointNmax*sizeof(double)); _u0 = (double *)malloc(ts_pointNmax * sizeof(double));
_u1 = (double*)malloc(ts_pointNmax*sizeof(double)); _u1 = (double *)malloc(ts_pointNmax * sizeof(double));
y = (double*)malloc(ts_pointNmax*sizeof(double)); y = (double *)malloc(ts_pointNmax * sizeof(double));
iv = (double*)malloc(ts_pointNmax*sizeof(double)); iv = (double *)malloc(ts_pointNmax * sizeof(double));
r = (double*)malloc(ts_pointNmax*sizeof(double)); r = (double *)malloc(ts_pointNmax * sizeof(double));
c = (double*)malloc(ts_pointNmax*sizeof(double)); c = (double *)malloc(ts_pointNmax * sizeof(double));
par = (int*)malloc(ts_pointNmax*sizeof(int)); par = (int *)malloc(ts_pointNmax * sizeof(int));
} }
if (ts_edgeN > ts_edgeNmax) { if (ts_edgeN > ts_edgeNmax) {
free(ts_edgeV); free(ts_edgeV);
free(ts_eV); free(ts_eV);
free(ts_stackV); free(ts_stackV);
ts_edgeNmax = ts_edgeN + ts_edge_count_incr_; ts_edgeNmax = ts_edgeN + ts_edge_count_incr_;
ts_edgeV = (ts_edge*)malloc(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_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *));
ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*)); ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *));
} }
} }
@ -302,21 +303,22 @@ ArnoldiReduce::allocTerms(int nterms)
free(outV); free(outV);
free(termV); free(termV);
free(pinV); free(pinV);
termNmax = nterms+256; termNmax = nterms + 256;
pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*)); pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *));
termV = (int*)malloc(termNmax*sizeof(int)); termV = (int *)malloc(termNmax * sizeof(int));
outV = (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; 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 * ts_point *
ArnoldiReduce::findPt(ParasiticNode *node) ArnoldiReduce::findPt(ParasiticNode *node)
{ {
return &ts_pointV[pt_map_[reinterpret_cast<ConcreteParasiticNode*>(node)]]; return &ts_pointV[pt_map_[reinterpret_cast<ConcreteParasiticNode *>(node)]];
} }
rcmodel * rcmodel *
@ -334,33 +336,36 @@ ArnoldiReduce::makeRcmodelDrv()
return makeRcmodelFromW(); return makeRcmodelFromW();
} }
#define ts_orient( pp, ee) \ #define ts_orient(pp, ee) \
if (ee->from!=pp) { ee->to = ee->from; ee->from = pp; } if (ee->from != pp) { \
ee->to = ee->from; \
ee->from = pp; \
}
void void
ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv) ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv)
{ {
bool loop = false; bool loop = false;
int k; int k;
ts_point *p,*q; ts_point *p, *q;
ts_point *p0 = ts_pointV; ts_point *p0 = ts_pointV;
ts_point *pend = p0 + ts_pointN; ts_point *pend = p0 + ts_pointN;
for (p=p0;p!=pend;p++) for (p = p0; p != pend; p++)
p->visited = 0; p->visited = 0;
ts_edge *e; ts_edge *e;
ts_edge **stackV = ts_stackV; ts_edge **stackV = ts_stackV;
int stackN = 1; int stackN = 1;
stackV[0] = e = pdrv->eV[0]; stackV[0] = e = pdrv->eV[0];
ts_orient(pdrv,e); ts_orient(pdrv, e);
pdrv->visited = 1; pdrv->visited = 1;
pdrv->in_edge = nullptr; pdrv->in_edge = nullptr;
pdrv->ts = 0; pdrv->ts = 0;
ts_ordV[0] = pdrv-p0; ts_ordV[0] = pdrv - p0;
ts_pordV[0] = pdrv; ts_pordV[0] = pdrv;
ts_ordN = 1; ts_ordN = 1;
while (stackN>0) { while (stackN > 0) {
e = stackV[stackN-1]; e = stackV[stackN - 1];
q = e->to; q = e->to;
if (q->visited) { if (q->visited) {
@ -368,38 +373,45 @@ ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv)
// ignore, and do not even set *loop // ignore, and do not even set *loop
if (e->to != e->from) if (e->to != e->from)
loop = true; loop = true;
} else { }
else {
// try to descend // try to descend
q->visited = 1; q->visited = 1;
q->ts = ts_ordN++; q->ts = ts_ordN++;
ts_pordV[q->ts] = q; ts_pordV[q->ts] = q;
ts_ordV[q->ts] = q-p0; ts_ordV[q->ts] = q - p0;
q->in_edge = e; q->in_edge = e;
if (q->eN>1) { if (q->eN > 1) {
for (k=0;k<q->eN;k++) if (q->eV[k] != e) break; for (k = 0; k < q->eN; k++)
if (q->eV[k] != e)
break;
e = q->eV[k]; e = q->eV[k];
ts_orient(q,e); ts_orient(q, e);
stackV[stackN++] = e; stackV[stackN++] = e;
continue; // descent continue; // descent
} }
} }
// try to ascend // try to ascend
while (--stackN>=0) { while (--stackN >= 0) {
e = stackV[stackN]; e = stackV[stackN];
p = e->from; p = e->from;
// find e in p->eV // 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"); // if (k==p->eN) notice(0,"ERROR, e not found!\n");
++k; ++k;
if (k>=p->eN) continue; if (k >= p->eN)
continue;
e = p->eV[k]; e = p->eV[k];
// check that next sibling is not the incoming edge // check that next sibling is not the incoming edge
if (stackN>0 && e==stackV[stackN-1]) { if (stackN > 0 && e == stackV[stackN - 1]) {
++k; ++k;
if (k>=p->eN) continue; if (k >= p->eN)
continue;
e = p->eV[k]; e = p->eV[k];
} }
ts_orient(p,e); ts_orient(p, e);
stackV[stackN++] = e; stackV[stackN++] = e;
break; break;
} }
@ -407,8 +419,7 @@ ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv)
} // while (stackN) } // while (stackN)
if (loop) if (loop)
debugPrint(debug_, "arnoldi", 1, "net %s loop", debugPrint(debug_, "arnoldi", 1, "net {} loop", network_->pathName(drvr_pin_));
network_->pathName(drvr_pin_));
} }
// makeRcmodelGetRC // makeRcmodelGetRC
@ -418,13 +429,12 @@ ArnoldiReduce::getRC()
ts_point *p, *p0 = ts_pointV; ts_point *p, *p0 = ts_pointV;
ts_point *pend = p0 + ts_pointN; ts_point *pend = p0 + ts_pointN;
ctot_ = 0.0; ctot_ = 0.0;
for (p=p0;p!=pend;p++) { for (p = p0; p != pend; p++) {
p->c = 0.0; p->c = 0.0;
p->r = 0.0; p->r = 0.0;
if (p->node_) { if (p->node_) {
ParasiticNode *node = p->node_; ParasiticNode *node = p->node_;
double cap = parasitics_->nodeGndCap(node) double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
+ pinCapacitance(node);
if (cap > 0.0) { if (cap > 0.0) {
p->c = cap; p->c = cap;
ctot_ += cap; ctot_ += cap;
@ -433,11 +443,9 @@ ArnoldiReduce::getRC()
p->c = 0.0; p->c = 0.0;
if (p->in_edge && p->in_edge->resistor_) if (p->in_edge && p->in_edge->resistor_)
p->r = parasitics_->value(p->in_edge->resistor_); p->r = parasitics_->value(p->in_edge->resistor_);
if (!(p->r>=0.0 && p->r<100e+3)) { // 0 < r < 100kohm if (!(p->r >= 0.0 && p->r < 100e+3)) { // 0 < r < 100kohm
debugPrint(debug_, "arnoldi", 1, debugPrint(debug_, "arnoldi", 1, "R value {:g} out of range, drvr pin {}",
"R value %g out of range, drvr pin %s", p->r, network_->pathName(drvr_pin_));
p->r,
network_->pathName(drvr_pin_));
} }
} }
} }
@ -466,7 +474,7 @@ ArnoldiReduce::pinCapacitance(ParasiticNode *node)
LibertyPort *lib_port = network_->libertyPort(port); LibertyPort *lib_port = network_->libertyPort(port);
const Sdc *sdc = scene_->sdc(); const Sdc *sdc = scene_->sdc();
if (lib_port) 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)) else if (network_->isTopLevelPort(pin))
pin_cap = sdc->portExtCap(port, rf_, min_max_); pin_cap = sdc->portExtCap(port, rf_, min_max_);
} }
@ -479,13 +487,15 @@ ArnoldiReduce::setTerms(ts_point *pdrv)
// termV: from drv-ordered to fixed order // termV: from drv-ordered to fixed order
// outV: from drv-ordered to ts_pordV // outV: from drv-ordered to ts_pordV
ts_point *p; ts_point *p;
int k,k0; int k, k0;
termV[0] = k0 = pdrv->tindex; termV[0] = k0 = pdrv->tindex;
for (k=1;k<termN;k++) { for (k = 1; k < termN; k++) {
if (k==k0) termV[k] = 0; if (k == k0)
else termV[k] = k; termV[k] = 0;
else
termV[k] = k;
} }
for (k=0;k<termN;k++) { for (k = 0; k < termN; k++) {
p = pterm0 + termV[k]; p = pterm0 + termV[k];
outV[k] = p->ts; outV[k] = p->ts;
} }
@ -498,38 +508,37 @@ ArnoldiReduce::makeRcmodelFromTs()
ts_point *p, *p0 = ts_pointV; ts_point *p, *p0 = ts_pointV;
int n = ts_ordN; int n = ts_ordN;
int nterms = termN; int nterms = termN;
int i,j,k,h; int i, j, k, h;
if (debug_->check("arnoldi", 1)) { if (debug_->check("arnoldi", 1)) {
for (k=0;k<ts_ordN;k++) { for (k = 0; k < ts_ordN; k++) {
p = ts_pordV[k]; p = ts_pordV[k];
debugPrint(debug_, "arnoldi", 1, "T%d,P%ld c=%s", debugPrint(debug_, "arnoldi", 1, "T{} P{} c={}", p->ts, p - p0,
p->ts,
p-p0,
units_->capacitanceUnit()->asString(p->c)); units_->capacitanceUnit()->asString(p->c));
if (p->is_term) if (p->is_term)
debugPrint(debug_, "arnoldi", 1, " term %d", p->tindex); debugPrint(debug_, "arnoldi", 1, " term {}", p->tindex);
if (p->in_edge) if (p->in_edge)
debugPrint(debug_, "arnoldi", 1, " from T%d,P%ld r=%s", debugPrint(debug_, "arnoldi", 1, " from T{} P{} r={}",
p->in_edge->from->ts, p->in_edge->from->ts, p->in_edge->from - p0,
p->in_edge->from-p0,
units_->resistanceUnit()->asString(p->r)); units_->resistanceUnit()->asString(p->r));
} }
for (i=0;i<nterms;i++) for (i = 0; i < nterms; i++)
debugPrint(debug_, "arnoldi", 1, "outV[%d] = T%d", i, outV[i]); debugPrint(debug_, "arnoldi", 1, "outV[{}] = T{}", i, outV[i]);
} }
int max_order = 5; int max_order = 5;
double *u0, *u1; double *u0, *u1;
u0 = _u0; u1 = _u1; u0 = _u0;
double sum,e1; u1 = _u1;
double sum, e1;
order = max_order; order = max_order;
if (n < order) if (n < order)
order = n; order = n;
par[0] = -1; r[0] = 0.0; par[0] = -1;
r[0] = 0.0;
c[0] = ts_pordV[0]->c; c[0] = ts_pordV[0]->c;
for (j=1;j<n;j++) { for (j = 1; j < n; j++) {
p = ts_pordV[j]; p = ts_pordV[j];
c[j] = p->c; c[j] = p->c;
r[j] = p->r; r[j] = p->r;
@ -537,92 +546,99 @@ ArnoldiReduce::makeRcmodelFromTs()
} }
sum = 0.0; sum = 0.0;
for (j=0;j<n;j++) sum += c[j]; for (j = 0; j < n; j++)
debugPrint(debug_, "arnoldi", 1, "ctot = %s", sum += c[j];
debugPrint(debug_, "arnoldi", 1, "ctot = {}",
units_->capacitanceUnit()->asString(sum)); units_->capacitanceUnit()->asString(sum));
ctot_ = sum; ctot_ = sum;
sqc_ = sqrt(sum); sqc_ = sqrt(sum);
double sqrt_ctot_inv = 1.0/sqc_; double sqrt_ctot_inv = 1.0 / sqc_;
for (j=0;j<n;j++) u0[j] = sqrt_ctot_inv; for (j = 0; j < n; j++)
for (h=0;h<order;h++) { u0[j] = sqrt_ctot_inv;
for (i=0;i<nterms;i++) U[h][i] = u0[outV[i]]; for (h = 0; h < order; h++) {
for (i = 0; i < nterms; i++)
U[h][i] = u0[outV[i]];
// y = R C u0 // y = R C u0
for (j=0;j<n;j++) { for (j = 0; j < n; j++) {
iv[j] = 0.0; iv[j] = 0.0;
} }
for (j=n-1;j>0;j--) { for (j = n - 1; j > 0; j--) {
iv[j] += c[j]*u0[j]; iv[j] += c[j] * u0[j];
iv[par[j]] += iv[j]; iv[par[j]] += iv[j];
} }
iv[0] += c[0]*u0[0]; iv[0] += c[0] * u0[0];
y[0] = 0.0; y[0] = 0.0;
for (j=1;j<n;j++) { for (j = 1; j < n; j++) {
y[j] = y[par[j]] + r[j]*iv[j]; y[j] = y[par[j]] + r[j] * iv[j];
} }
// d[h] = u0 C y // d[h] = u0 C y
sum = 0.0; sum = 0.0;
for (j=1;j<n;j++) { for (j = 1; j < n; j++) {
sum += u0[j]*c[j]*y[j]; sum += u0[j] * c[j] * y[j];
} }
d[h] = sum; d[h] = sum;
if (h==order-1) break; if (h == order - 1)
if (d[h]<1e-13) { // .1ps break;
order = h+1; if (d[h] < 1e-13) { // .1ps
order = h + 1;
break; break;
} }
// y = y - d[h]*u0 - e[h-1]*u1 // y = y - d[h]*u0 - e[h-1]*u1
if (h==0) { if (h == 0) {
for (j=0;j<n;j++) y[j] -= sum*u0[j]; for (j = 0; j < n; j++)
} else { y[j] -= sum * u0[j];
e1 = e[h-1]; }
for (j=0;j<n;j++) y[j] -= sum*u0[j] + e1*u1[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) // e[h] = sqrt(y C y)
// u1 = y/e[h] // u1 = y/e[h]
sum = 0.0; sum = 0.0;
for (j=0;j<n;j++) { for (j = 0; j < n; j++) {
sum += c[j]*y[j]*y[j]; sum += c[j] * y[j] * y[j];
} }
if (sum<1e-30) { // (1e-6ns)^2 if (sum < 1e-30) { // (1e-6ns)^2
order = h+1; order = h + 1;
break; break;
} }
e[h] = sqrt(sum); e[h] = sqrt(sum);
sum = 1.0/e[h]; sum = 1.0 / e[h];
for (j=0;j<n;j++) u1[j] = sum*y[j]; for (j = 0; j < n; j++)
u1[j] = sum * y[j];
// swap u0, u1 // swap u0, u1
if (h%2) { if (h % 2) {
u0 = _u0; u1 = _u1; u0 = _u0;
} else { u1 = _u1;
u0 = _u1; u1 = _u0; }
else {
u0 = _u1;
u1 = _u0;
} }
} }
if (debug_->check("arnoldi", 1)) { if (debug_->check("arnoldi", 1)) {
report_->reportLine("tridiagonal reduced matrix, drvr pin %s", report_->report("tridiagonal reduced matrix, drvr pin {}",
network_->pathName(drvr_pin_)); network_->pathName(drvr_pin_));
report_->reportLine("order %d n %d",order,n); report_->report("order {} n {}", order, n);
for (h=0;h<order;h++) { for (h = 0; h < order; h++) {
if (h<order-1) if (h < order - 1)
report_->reportLine(" d[%d] %s e[%d] %s", report_->report(" d[{}] {} e[{}] {}", h,
h, units_->timeUnit()->asString(d[h]), h,
units_->timeUnit()->asString(d[h]),
h,
units_->timeUnit()->asString(e[h])); units_->timeUnit()->asString(e[h]));
else else
report_->reportLine(" d[%d] %s", report_->report(" d[{}] {}", h, units_->timeUnit()->asString(d[h]));
h, std::string line = sta::format("U[{}]", h);
units_->timeUnit()->asString(d[h])); for (i = 0; i < nterms; i++)
std::string line = stdstrPrint("U[%d]",h); line += sta::format(" {:6.2e}", U[h][i]);
for (i=0;i<nterms;i++) report_->reportLine(line);
line += stdstrPrint(" %6.2e",U[h][i]);
report_->reportLineString(line);
} }
} }
} }
@ -630,29 +646,33 @@ ArnoldiReduce::makeRcmodelFromTs()
rcmodel * rcmodel *
ArnoldiReduce::makeRcmodelFromW() ArnoldiReduce::makeRcmodelFromW()
{ {
int j,h; int j, h;
int n = termN; int n = termN;
rcmodel *mod = new rcmodel(); rcmodel *mod = new rcmodel();
mod->order = order; mod->order = order;
mod->n = n; mod->n = n;
if (order>0) { if (order > 0) {
int totd = order + order - 1 + order*n; int totd = order + order - 1 + order * n;
mod->d = (double *)malloc(totd*sizeof(double)); mod->d = (double *)malloc(totd * sizeof(double));
if (order>1) mod->e = mod->d + order; if (order > 1)
else mod->e = nullptr; mod->e = mod->d + order;
mod->U = (double **)malloc(order*sizeof(double*)); else
mod->e = nullptr;
mod->U = (double **)malloc(order * sizeof(double *));
mod->U[0] = mod->d + order + order - 1; mod->U[0] = mod->d + order + order - 1;
for (h=1;h<order;h++) mod->U[h]=mod->U[0] + h*n; for (h = 1; h < order; h++)
for (h=0;h<order;h++) { mod->U[h] = mod->U[0] + h * n;
for (h = 0; h < order; h++) {
mod->d[h] = d[h]; mod->d[h] = d[h];
if (h<order-1) mod->e[h] = e[h]; if (h < order - 1)
for (j=0;j<n;j++) mod->e[h] = e[h];
for (j = 0; j < n; j++)
mod->U[h][j] = U[h][j]; mod->U[h][j] = U[h][j];
} }
} }
mod->pinV = (const Pin **)malloc(n*sizeof(const Pin*)); mod->pinV = (const Pin **)malloc(n * sizeof(const Pin *));
for (j=0;j<n;j++) { for (j = 0; j < n; j++) {
int k = termV[j]; int k = termV[j];
mod->pinV[j] = pinV[k]; mod->pinV[j] = pinV[k];
} }
@ -662,4 +682,4 @@ ArnoldiReduce::makeRcmodelFromW()
return mod; return mod;
} }
} // namespace } // namespace sta

View File

@ -63,10 +63,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
{ {
} }
CcsCeffDelayCalc::~CcsCeffDelayCalc() CcsCeffDelayCalc::~CcsCeffDelayCalc() { delete table_dcalc_; }
{
delete table_dcalc_;
}
ArcDelayCalc * ArcDelayCalc *
CcsCeffDelayCalc::copy() CcsCeffDelayCalc::copy()
@ -90,13 +87,13 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
parasitic_ = parasitic; parasitic_ = parasitic;
output_waveforms_ = nullptr; output_waveforms_ = nullptr;
GateTableModel *table_model = arc->gateTableModel(scene, min_max); const GateTableModel *table_model = arc->gateTableModel(scene, min_max);
if (table_model && parasitic) { if (table_model && parasitic) {
OutputWaveforms *output_waveforms = table_model->outputWaveforms(); OutputWaveforms *output_waveforms = table_model->outputWaveforms();
Parasitics *parasitics = scene->parasitics(min_max); Parasitics *parasitics = scene->parasitics(min_max);
parasitics->piModel(parasitic, c2_, rpi_, c1_); parasitics->piModel(parasitic, c2_, rpi_, c1_);
if (output_waveforms if (output_waveforms && rpi_ > 0.0
&& rpi_ > 0.0 && c1_ > 0.0 && c1_ > 0.0
// Bounds check because extrapolating waveforms does not work for shit. // Bounds check because extrapolating waveforms does not work for shit.
&& output_waveforms->slewAxis()->inBounds(in_slew_) && output_waveforms->slewAxis()->inBounds(in_slew_)
&& output_waveforms->capAxis()->inBounds(c2_) && output_waveforms->capAxis()->inBounds(c2_)
@ -107,22 +104,44 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
drvr_rf_ = arc->toEdge()->asRiseFall(); drvr_rf_ = arc->toEdge()->asRiseFall();
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!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_; vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_; vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_; vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
drvr_cell->ensureVoltageWaveforms(scenes_); drvr_cell->ensureVoltageWaveforms(scenes_);
in_slew_ = delayAsFloat(in_slew);
output_waveforms_ = output_waveforms; output_waveforms_ = output_waveforms;
ref_time_ = output_waveforms_->referenceTime(in_slew_); ref_time_ = output_waveforms_->referenceTime(in_slew_);
debugPrint(debug_, "ccs_dcalc", 1, "%s %s", debugPrint(debug_, "ccs_dcalc", 1, "{} {}",
drvr_cell->name(), drvr_cell->name(),
drvr_rf_->shortName()); drvr_rf_->shortName());
ArcDelay gate_delay;
Slew drvr_slew; double gate_delay, drvr_slew;
gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew); gateDelaySlew(drvr_library, gate_delay, drvr_slew);
return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map); 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, return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
@ -131,24 +150,23 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
void void
CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
const RiseFall *rf,
// Return values. // Return values.
ArcDelay &gate_delay, double &gate_delay,
Slew &drvr_slew) double &drvr_slew)
{ {
initRegions(drvr_library, rf); initRegions(drvr_library, drvr_rf_);
findCsmWaveform(); findCsmWaveform();
ref_time_ = output_waveforms_->referenceTime(in_slew_); ref_time_ = output_waveforms_->referenceTime(in_slew_);
gate_delay = region_times_[region_vth_idx_] - ref_time_; gate_delay = region_times_[region_vth_idx_] - ref_time_;
drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
debugPrint(debug_, "ccs_dcalc", 2, debugPrint(debug_, "ccs_dcalc", 2,
"gate_delay %s drvr_slew %s (initial)", "gate_delay {} drvr_slew {} (initial)",
delayAsString(gate_delay, this), delayAsString(gate_delay, this),
delayAsString(drvr_slew, this)); delayAsString(drvr_slew, this));
float prev_drvr_slew = delayAsFloat(drvr_slew); float prev_drvr_slew = drvr_slew;
constexpr int max_iterations = 5; constexpr int max_iterations = 5;
for (int iter = 0; iter < max_iterations; iter++) { 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. // Init drvr ramp model for vl.
for (size_t i = 0; i <= region_count_; i++) { for (size_t i = 0; i <= region_count_; i++) {
region_ramp_times_[i] = region_times_[i]; region_ramp_times_[i] = region_times_[i];
@ -174,20 +192,19 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
double q2 = v2 * c2_ + c1_v2 * c1_; double q2 = v2 * c2_ + c1_v2 * c1_;
double ceff = (q2 - q1) / (v2 - v1); double ceff = (q2 - q1) / (v2 - v1);
debugPrint(debug_, "ccs_dcalc", 2, "ceff %s", debugPrint(debug_, "ccs_dcalc", 2, "ceff {}",
capacitance_unit_->asString(ceff)); capacitance_unit_->asString(ceff));
region_ceff_[i] = ceff; region_ceff_[i] = ceff;
} }
findCsmWaveform(); findCsmWaveform();
gate_delay = region_times_[region_vth_idx_] - ref_time_; gate_delay = region_times_[region_vth_idx_] - ref_time_;
drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]); drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
debugPrint(debug_, "ccs_dcalc", 2, debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}",
"gate_delay %s drvr_slew %s",
delayAsString(gate_delay, this), delayAsString(gate_delay, this),
delayAsString(drvr_slew, 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; break;
prev_drvr_slew = delayAsFloat(drvr_slew); prev_drvr_slew = drvr_slew;
} }
} }
@ -287,8 +304,8 @@ void
CcsCeffDelayCalc::findCsmWaveform() CcsCeffDelayCalc::findCsmWaveform()
{ {
for (size_t i = 0; i < region_count_; i++) { for (size_t i = 0; i < region_count_; i++) {
double t1 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i], double t1 =
region_volts_[i]); output_waveforms_->voltageTime(in_slew_, region_ceff_[i], region_volts_[i]);
double t2 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i], double t2 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
region_volts_[i + 1]); region_volts_[i + 1]);
region_begin_times_[i] = t1; region_begin_times_[i] = t1;
@ -306,68 +323,42 @@ 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 void
CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
const LibertyLibrary *drvr_library, const LibertyLibrary *drvr_library,
const RiseFall *rf, double drvr_slew,
Slew &drvr_slew,
// Return values. // Return values.
ArcDelay &wire_delay, double &wire_delay,
Slew &load_slew) double &load_slew)
{ {
wire_delay = 0.0; wire_delay = 0.0;
load_slew = drvr_slew; load_slew = drvr_slew;
bool elmore_exists = false; bool elmore_exists = false;
float elmore = 0.0; float elmore = 0.0;
if (parasitic_ if (parasitic_ && parasitics_->isPiElmore(parasitic_))
&& parasitics_->isPiElmore(parasitic_))
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
if (elmore_exists && if (elmore_exists &&
(elmore == 0.0 (elmore == 0.0
// Elmore delay is small compared to driver slew. // Elmore delay is small compared to driver slew.
|| elmore < delayAsFloat(drvr_slew) * 1e-3)) { || elmore < drvr_slew * 1e-3)) {
wire_delay = elmore; wire_delay = elmore;
load_slew = drvr_slew; load_slew = drvr_slew;
} }
else else
loadDelaySlew(load_pin, drvr_slew, elmore, wire_delay, load_slew); 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 void
CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin, CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
Slew &drvr_slew, double &drvr_slew,
float elmore, float elmore,
// Return values. // Return values.
ArcDelay &delay, double &delay,
Slew &slew) double &slew)
{ {
for (size_t i = 0; i <= region_count_; i++) { for (size_t i = 0; i <= region_count_; i++) {
region_ramp_times_[i] = region_times_[i]; region_ramp_times_[i] = region_times_[i];
@ -389,10 +380,8 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
slew = drvr_slew; slew = drvr_slew;
fail("load delay threshold crossing"); fail("load delay threshold crossing");
} }
debugPrint(debug_, "ccs_dcalc", 2, debugPrint(debug_, "ccs_dcalc", 2, "load {} delay {} slew {}",
"load %s delay %s slew %s", network_->pathName(load_pin), delayAsString(delay, this),
network_->pathName(load_pin),
delayAsString(delay, this),
delayAsString(slew, this)); delayAsString(slew, this));
} }
@ -457,12 +446,12 @@ CcsCeffDelayCalc::findVlTime(double v,
double t_init = region_ramp_times_[0]; double t_init = region_ramp_times_[0];
double t_final = region_ramp_times_[region_count_]; double t_final = region_ramp_times_[region_count_];
bool root_fail = false; bool root_fail = false;
double time = findRoot([&] (double t, double time = findRoot(
double &y, [&](double t, double &y, double &dy) {
double &dy) {
vl(t, elmore, y, dy); vl(t, elmore, y, dy);
y -= v; y -= v;
}, t_init, t_final + elmore * 3.0, .001, 20, root_fail); },
t_init, t_final + elmore * 3.0, .001, 20, root_fail);
vl_fail_ |= root_fail; vl_fail_ |= root_fail;
return time; return time;
} }
@ -487,7 +476,7 @@ PinSeq
CcsCeffDelayCalc::watchPins() const CcsCeffDelayCalc::watchPins() const
{ {
PinSeq pins; PinSeq pins;
for (const auto& [pin, values] : watch_pin_values_) for (const auto &[pin, values] : watch_pin_values_)
pins.push_back(pin); pins.push_back(pin);
return pins; return pins;
} }
@ -523,8 +512,8 @@ CcsCeffDelayCalc::drvrWaveform()
drvr_volts->push_back(v); drvr_volts->push_back(v);
} }
} }
TableAxisPtr drvr_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time, TableAxisPtr drvr_time_axis =
std::move(*drvr_times)); std::make_shared<TableAxis>(TableAxisVariable::time, std::move(*drvr_times));
delete drvr_times; delete drvr_times;
Table drvr_table(drvr_volts, drvr_time_axis); Table drvr_table(drvr_volts, drvr_time_axis);
return drvr_table; return drvr_table;
@ -555,8 +544,8 @@ CcsCeffDelayCalc::loadWaveform(const Pin *load_pin)
double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v; double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v;
load_volts->push_back(v1); load_volts->push_back(v1);
} }
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time, TableAxisPtr load_time_axis = std::make_shared<TableAxis>(
std::move(*load_times)); TableAxisVariable::time, std::move(*load_times));
delete load_times; delete load_times;
Table load_table(load_volts, load_time_axis); Table load_table(load_volts, load_time_axis);
return load_table; return load_table;
@ -578,10 +567,9 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin,
float elmore = 0.0; float elmore = 0.0;
if (parasitic_) { if (parasitic_) {
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists); parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin, bool dcalc_success =
drvr_rf, scene, min_max); makeWaveformPreamble(in_pin, in_rf, drvr_pin, drvr_rf, scene, min_max);
if (dcalc_success if (dcalc_success && elmore_exists) {
&& elmore_exists) {
FloatSeq *load_times = new FloatSeq; FloatSeq *load_times = new FloatSeq;
FloatSeq *load_volts = new FloatSeq; FloatSeq *load_volts = new FloatSeq;
for (size_t j = 0; j <= region_count_; j++) { 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; double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
load_volts->push_back(v1); load_volts->push_back(v1);
} }
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time, TableAxisPtr load_time_axis = std::make_shared<TableAxis>(
std::move(*load_times)); TableAxisVariable::time, std::move(*load_times));
delete load_times; delete load_times;
Table load_table(load_volts, load_time_axis); Table load_table(load_volts, load_time_axis);
return load_table; return load_table;
@ -646,8 +634,8 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin,
parasitics_->piModel(parasitic_, c2_, rpi_, c1_); parasitics_->piModel(parasitic_, c2_, rpi_, c1_);
LoadPinIndexMap load_pin_index_map = LoadPinIndexMap load_pin_index_map =
graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex); graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_, gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_, load_pin_index_map,
load_pin_index_map, scene, min_max); scene, min_max);
return true; return true;
} }
} }
@ -671,12 +659,12 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
Parasitic *pi_elmore = nullptr; Parasitic *pi_elmore = nullptr;
const RiseFall *rf = arc->toEdge()->asRiseFall(); const RiseFall *rf = arc->toEdge()->asRiseFall();
if (parasitic && !parasitics_->isPiElmore(parasitic)) { if (parasitic && !parasitics_->isPiElmore(parasitic)) {
pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, pi_elmore =
scene, min_max); parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, scene, min_max);
} }
std::string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, std::string report =
pi_elmore, load_pin_index_map, table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, pi_elmore,
scene, min_max, digits); load_pin_index_map, scene, min_max, digits);
parasitics_->deleteDrvrReducedParasitics(drvr_pin); parasitics_->deleteDrvrReducedParasitics(drvr_pin);
return report; return report;
} }
@ -686,7 +674,7 @@ CcsCeffDelayCalc::fail(const char *reason)
{ {
// Report failures with a unique debug flag. // Report failures with a unique debug flag.
if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1)) 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; typedef std::vector<double> Region;
void gateDelaySlew(const LibertyLibrary *drvr_library, void gateDelaySlew(const LibertyLibrary *drvr_library,
const RiseFall *rf,
// Return values. // Return values.
ArcDelay &gate_delay, double &gate_delay,
Slew &drvr_slew); double &drvr_slew);
void initRegions(const LibertyLibrary *drvr_library, void initRegions(const LibertyLibrary *drvr_library,
const RiseFall *rf); const RiseFall *rf);
void findCsmWaveform(); 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, void loadDelaySlew(const Pin *load_pin,
const LibertyLibrary *drvr_library, const LibertyLibrary *drvr_library,
const RiseFall *rf, double drvr_slew,
Slew &drvr_slew,
// Return values. // Return values.
ArcDelay &wire_delay, double &wire_delay,
Slew &load_slew); double &load_slew);
void loadDelaySlew(const Pin *load_pin, void loadDelaySlew(const Pin *load_pin,
Slew &drvr_slew, double &drvr_slew,
float elmore, float elmore,
// Return values. // Return values.
ArcDelay &delay, double &delay,
Slew &slew); double &slew);
double findVlTime(double v, double findVlTime(double v,
double elmore); double elmore);
bool makeWaveformPreamble(const Pin *in_pin, 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] } { if { [is_delay_calc_name $alg] } {
set_delay_calculator_cmd $alg set_delay_calculator_cmd $alg
} else { } 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" \ define_cmd_args "set_assigned_delay" \
@ -156,38 +154,38 @@ proc set_assigned_delay { args } {
if [info exists keys(-from)] { if [info exists keys(-from)] {
set from_pins [get_port_pins_error "from_pins" $keys(-from)] set from_pins [get_port_pins_error "from_pins" $keys(-from)]
} else { } else {
sta_error 196 "set_assigned_delay missing -from argument." sta_error 2501 "set_assigned_delay missing -from argument."
} }
if [info exists keys(-to)] { if [info exists keys(-to)] {
set to_pins [get_port_pins_error "to_pins" $keys(-to)] set to_pins [get_port_pins_error "to_pins" $keys(-to)]
} else { } else {
sta_error 182 "set_assigned_delay missing -to argument." sta_error 2502 "set_assigned_delay missing -to argument."
} }
set delay [lindex $args 0] set delay [lindex $args 0]
if {![string is double $delay]} { 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] set delay [time_ui_sta $delay]
if {[info exists flags(-cell)] && [info exists flags(-net)]} { 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)]} { } elseif {[info exists flags(-cell)]} {
if { $from_pins != {} } { if { $from_pins != {} } {
set inst [[lindex $from_pins 0] instance] set inst [[lindex $from_pins 0] instance]
foreach pin $from_pins { foreach pin $from_pins {
if {[$pin instance] != $inst} { 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 { foreach pin $to_pins {
if {[$pin instance] != $inst} { 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)]} { } 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 { foreach from_pin $from_pins {
set from_vertices [$from_pin vertices] 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 $edge_iter finish
if { !$matched } { 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)] } { if { [info exists keys(-from)] } {
set from_pins [get_port_pins_error "from_pins" $keys(-from)] set from_pins [get_port_pins_error "from_pins" $keys(-from)]
} else { } else {
sta_error 188 "set_assigned_check missing -from argument." sta_error 2510 "set_assigned_check missing -from argument."
} }
set from_rf "rise_fall" set from_rf "rise_fall"
if { [info exists keys(-clock)] } { if { [info exists keys(-clock)] } {
@ -261,14 +259,14 @@ proc set_assigned_check { args } {
|| $clk_arg eq "fall" } { || $clk_arg eq "fall" } {
set from_rf $clk_arg set from_rf $clk_arg
} else { } 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)] } { if { [info exists keys(-to)] } {
set to_pins [get_port_pins_error "to_pins" $keys(-to)] set to_pins [get_port_pins_error "to_pins" $keys(-to)]
} else { } 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 to_rf [parse_rise_fall_flags flags]
set scene [parse_scene keys] set scene [parse_scene keys]
@ -283,7 +281,7 @@ proc set_assigned_check { args } {
} elseif { [info exists flags(-removal)] } { } elseif { [info exists flags(-removal)] } {
set role "removal" set role "removal"
} else { } 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 "" set cond ""
if { [info exists key(-cond)] } { if { [info exists key(-cond)] } {
@ -291,7 +289,7 @@ proc set_assigned_check { args } {
} }
set check_value [lindex $args 0] set check_value [lindex $args 0]
if { ![string is double $check_value] } { 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] 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 $edge_iter finish
if { !$matched } { 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] set slew [lindex $args 0]
if {![string is double $slew]} { 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 slew [time_ui_sta $slew]
set pins [get_port_pins_error "pins" [lindex $args 1]] 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 } { proc report_slews { args } {
global sta_report_default_digits 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 check_argc_eq1 "report_slews" $args
set scenes [parse_scenes_or_all keys] set scenes [parse_scenes_or_all keys]
set pin [get_port_pin_error "pin" [lindex $args 0]] set pin [get_port_pin_error "pin" [lindex $args 0]]
if [info exists keys(-digits)] {
set digits $keys(-digits)
check_positive_integer "-digits" $digits
} else {
set digits $sta_report_default_digits set digits $sta_report_default_digits
}
set report_variance [info exists flags(-report_variance)]
foreach vertex [$pin vertices] { foreach vertex [$pin vertices] {
set rise_min [format_time [$vertex slew_scenes rise $scenes min] $digits] set rise_min [$vertex slew_scenes_string rise $scenes min $report_variance $digits]
set rise_max [format_time [$vertex slew_scenes rise $scenes max] $digits] set rise_max [$vertex slew_scenes_string rise $scenes max $report_variance $digits]
set fall_min [format_time [$vertex slew_scenes fall $scenes min] $digits] set fall_min [$vertex slew_scenes_string fall $scenes min $report_variance $digits]
set fall_max [format_time [$vertex slew_scenes fall $scenes max] $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" 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 void
DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin, DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin,
const RiseFall *rf, const RiseFall *rf,
Slew drvr_slew, double drvr_slew,
float elmore, float elmore,
ArcDelay &wire_delay, double &wire_delay,
Slew &load_slew) double &load_slew)
{ {
LibertyLibrary *load_library = thresholdLibrary(load_pin); LibertyLibrary *load_library = thresholdLibrary(load_pin);
@ -107,8 +107,8 @@ void
DelayCalcBase::thresholdAdjust(const Pin *load_pin, DelayCalcBase::thresholdAdjust(const Pin *load_pin,
const LibertyLibrary *drvr_library, const LibertyLibrary *drvr_library,
const RiseFall *rf, const RiseFall *rf,
ArcDelay &load_delay, double &wire_delay,
Slew &load_slew) double &load_slew)
{ {
LibertyLibrary *load_library = thresholdLibrary(load_pin); LibertyLibrary *load_library = thresholdLibrary(load_pin);
if (load_library if (load_library
@ -118,11 +118,12 @@ DelayCalcBase::thresholdAdjust(const Pin *load_pin,
float load_vth = load_library->inputThreshold(rf); float load_vth = load_library->inputThreshold(rf);
float drvr_slew_delta = drvr_library->slewUpperThreshold(rf) float drvr_slew_delta = drvr_library->slewUpperThreshold(rf)
- drvr_library->slewLowerThreshold(rf); - drvr_library->slewLowerThreshold(rf);
float load_delay_delta = float wire_delay_delta =
delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta); delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta);
load_delay += (rf == RiseFall::rise()) wire_delay += (rf == RiseFall::rise())
? load_delay_delta ? wire_delay_delta
: -load_delay_delta; : -wire_delay_delta;
float load_slew_delta = load_library->slewUpperThreshold(rf) float load_slew_delta = load_library->slewUpperThreshold(rf)
- load_library->slewLowerThreshold(rf); - load_library->slewLowerThreshold(rf);
float drvr_slew_derate = drvr_library->slewDerateFromLibrary(); float drvr_slew_derate = drvr_library->slewDerateFromLibrary();
@ -162,9 +163,8 @@ DelayCalcBase::checkDelay(const Pin *check_pin,
float from_slew1 = delayAsFloat(from_slew); float from_slew1 = delayAsFloat(from_slew);
float to_slew1 = delayAsFloat(to_slew); float to_slew1 = delayAsFloat(to_slew);
return model->checkDelay(pinPvt(check_pin, scene, min_max), return model->checkDelay(pinPvt(check_pin, scene, min_max),
from_slew1, to_slew1, from_slew1, to_slew1, related_out_cap,
related_out_cap, min_max, variables_->pocvMode());
variables_->pocvEnabled());
} }
else else
return delay_zero; return delay_zero;
@ -187,8 +187,8 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin,
float to_slew1 = delayAsFloat(to_slew); float to_slew1 = delayAsFloat(to_slew);
return model->reportCheckDelay(pinPvt(check_pin, scene, min_max), return model->reportCheckDelay(pinPvt(check_pin, scene, min_max),
from_slew1, from_slew_annotation, from_slew1, from_slew_annotation,
to_slew1, related_out_cap, false, to_slew1, related_out_cap, min_max,
digits); PocvMode::scalar, digits);
} }
return ""; return "";
} }

View File

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

@ -36,6 +36,7 @@
#include <cmath> #include <cmath>
#include <functional> #include <functional>
#include "Format.hh"
#include "Report.hh" #include "Report.hh"
#include "Debug.hh" #include "Debug.hh"
#include "Units.hh" #include "Units.hh"
@ -90,15 +91,14 @@ gateModelRd(const LibertyCell *cell,
double in_slew, double in_slew,
double c2, double c2,
double c1, double c1,
const Pvt *pvt, const Pvt *pvt);
bool pocv_enabled);
static void static void
newtonRaphson(const int max_iter, newtonRaphson(const int max_iter,
double x[], double x[],
const int n, const int n,
const double x_tol, const double x_tol,
// eval(state) is called to fill fvec and fjac. // eval(state) is called to fill fvec and fjac.
std::function<void ()> eval, std::function<void()> eval,
// Temporaries supplied by caller. // Temporaries supplied by caller.
double *fvec, double *fvec,
double **fjac, double **fjac,
@ -123,7 +123,8 @@ luDecomp(double **a,
class DmpAlg : public StaState class DmpAlg : public StaState
{ {
public: public:
DmpAlg(int nr_order, StaState *sta); DmpAlg(int nr_order,
StaState *sta);
~DmpAlg() override = default; ~DmpAlg() override = default;
virtual const char *name() = 0; virtual const char *name() = 0;
// Set driver model and pi model parameters for delay calculation. // Set driver model and pi model parameters for delay calculation.
@ -137,14 +138,14 @@ public:
double c2, double c2,
double rpi, double rpi,
double c1); double c1);
virtual void gateDelaySlew(// Return values. virtual void gateDelaySlew( // Return values.
double &delay, double &delay,
double &slew) = 0; double &slew) = 0;
virtual void loadDelaySlew(const Pin *load_pin, virtual void loadDelaySlew(const Pin *load_pin,
double elmore, double elmore,
// Return values. // Return values.
ArcDelay &delay, double &delay,
Slew &slew); double &slew);
double ceff() { return ceff_; } double ceff() { return ceff_; }
// Given x_ as a vector of input parameters, fill fvec_ with the // Given x_ as a vector of input parameters, fill fvec_ with the
@ -189,7 +190,7 @@ protected:
void showX(); void showX();
void showFvec(); void showFvec();
void showJacobian(); void showJacobian();
void findDriverDelaySlew(// Return values. void findDriverDelaySlew( // Return values.
double &delay, double &delay,
double &slew); double &slew);
double findVoCrossing(double vth, double findVoCrossing(double vth,
@ -261,7 +262,7 @@ protected:
double fjac_storage_[max_nr_order_ * max_nr_order_]; double fjac_storage_[max_nr_order_ * max_nr_order_];
double *fjac_[max_nr_order_]; double *fjac_[max_nr_order_];
double scale_[max_nr_order_]; double scale_[max_nr_order_];
double p_[max_nr_order_ ]; double p_[max_nr_order_];
int index_[max_nr_order_]; int index_[max_nr_order_];
// Driver slew used to check load delay. // Driver slew used to check load delay.
@ -275,7 +276,7 @@ protected:
}; };
DmpAlg::DmpAlg(int nr_order, DmpAlg::DmpAlg(int nr_order,
StaState *sta): StaState *sta) :
StaState(sta), StaState(sta),
c2_(0.0), c2_(0.0),
rpi_(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; double t0 = t_vth + std::log(1.0 - vth_) * rd_ * ceff - vth_ * dt;
x_[DmpParam::dt] = dt; x_[DmpParam::dt] = dt;
x_[DmpParam::t0] = t0; x_[DmpParam::t0] = t0;
newtonRaphson(100, x_, nr_order_, driver_param_tol, newtonRaphson(
[this] () { evalDmpEqns(); }, 100, x_, nr_order_, driver_param_tol, [this]() { evalDmpEqns(); }, fvec_,
fvec_, fjac_, index_, p_, scale_); fjac_, index_, p_, scale_);
t0_ = x_[DmpParam::t0]; t0_ = x_[DmpParam::t0];
dt_ = x_[DmpParam::dt]; dt_ = x_[DmpParam::dt];
debugPrint(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s", debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}",
units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_),
units_->timeUnit()->asString(dt_),
units_->capacitanceUnit()->asString(x_[DmpParam::ceff])); units_->capacitanceUnit()->asString(x_[DmpParam::ceff]));
if (debug_->check("dmp_ceff", 4)) if (debug_->check("dmp_ceff", 4))
showVo(); showVo();
@ -348,13 +348,10 @@ DmpAlg::gateCapDelaySlew(double ceff,
double &delay, double &delay,
double &slew) double &slew)
{ {
ArcDelay model_delay; float model_delay, model_slew;
Slew model_slew; gate_model_->gateDelay(pvt_, in_slew_, ceff, model_delay, model_slew);
gate_model_->gateDelay(pvt_, in_slew_, ceff, delay = model_delay;
variables_->pocvEnabled(), slew = model_slew;
model_delay, model_slew);
delay = delayAsFloat(model_delay);
slew = delayAsFloat(model_slew);
} }
void void
@ -413,8 +410,7 @@ DmpAlg::dy(double t,
} }
else { else {
dydt0 = -(y0dt(t1, cl) - y0dt(t1 - dt, cl)) / dt; dydt0 = -(y0dt(t1, cl) - y0dt(t1 - dt, cl)) / dt;
dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) + y0dt(t1 - dt, cl) / dt;
+ y0dt(t1 - dt, cl) / dt;
dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt; dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt;
} }
} }
@ -437,14 +433,14 @@ void
DmpAlg::showX() DmpAlg::showX()
{ {
for (int i = 0; i < nr_order_; i++) 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 void
DmpAlg::showFvec() DmpAlg::showFvec()
{ {
for (int i = 0; i < nr_order_; i++) 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 void
@ -452,19 +448,19 @@ DmpAlg::showJacobian()
{ {
std::string line = " "; std::string line = " ";
for (int j = 0; j < nr_order_; j++) for (int j = 0; j < nr_order_; j++)
line += stdstrPrint("%12s", dmp_param_index_strings[j]); line += sta::format("{:>12}", dmp_param_index_strings[j]);
report_->reportLineString(line); report_->reportLine(line);
line.clear();
for (int i = 0; i < nr_order_; i++) { 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++) for (int j = 0; j < nr_order_; j++)
line += stdstrPrint("%12.3e ", fjac_[i][j]); line += sta::format("{:12.3e} ", fjac_[i][j]);
report_->reportLineString(line); report_->reportLine(line);
} }
} }
void void
DmpAlg::findDriverDelaySlew(// Return values. DmpAlg::findDriverDelaySlew( // Return values.
double &delay, double &delay,
double &slew) double &slew)
{ {
@ -482,17 +478,15 @@ DmpAlg::findVoCrossing(double vth,
double t_lower, double t_lower,
double t_upper) double t_upper)
{ {
FindRootFunc vo_func = [&] (double t, FindRootFunc vo_func = [&](double t, double &y, double &dy) {
double &y,
double &dy) {
double vo, vo_dt; double vo, vo_dt;
Vo(t, vo, vo_dt); Vo(t, vo, vo_dt);
y = vo - vth; y = vo - vth;
dy = vo_dt; dy = vo_dt;
}; };
bool fail; bool fail;
double t_vth = findRoot(vo_func, t_lower, t_upper, vth_time_tol, double t_vth =
find_root_max_iter, fail); findRoot(vo_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail);
if (fail) if (fail)
throw DmpError("find Vo crossing failed"); throw DmpError("find Vo crossing failed");
return t_vth; return t_vth;
@ -531,20 +525,20 @@ DmpAlg::Vo(double t,
void void
DmpAlg::showVo() DmpAlg::showVo()
{ {
report_->reportLine(" t vo(t)"); report_->report(" t vo(t)");
double ub = voCrossingUpperBound(); double ub = voCrossingUpperBound();
for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) { for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) {
double vo, dvo_dt; double vo, dvo_dt;
Vo(t, vo, dvo_dt); Vo(t, vo, dvo_dt);
report_->reportLine(" %g %g", t, vo); report_->report(" {:g} {:g}", t, vo);
} }
} }
void void
DmpAlg::loadDelaySlew(const Pin *, DmpAlg::loadDelaySlew(const Pin *,
double elmore, double elmore,
ArcDelay &delay, double &delay,
Slew &slew) double &slew)
{ {
if (!driver_valid_ if (!driver_valid_
|| elmore == 0.0 || elmore == 0.0
@ -557,10 +551,10 @@ DmpAlg::loadDelaySlew(const Pin *,
// Use the driver thresholds and rely on thresholdAdjust to // Use the driver thresholds and rely on thresholdAdjust to
// convert the delay and slew to the load's thresholds. // convert the delay and slew to the load's thresholds.
try { try {
if (debug_->check("dmp_ceff", 4))
showVl();
elmore_ = elmore; elmore_ = elmore;
p3_ = 1.0 / elmore; p3_ = 1.0 / elmore;
if (debug_->check("dmp_ceff", 4))
showVl();
double t_lower = t0_; double t_lower = t0_;
double t_upper = vlCrossingUpperBound(); double t_upper = vlCrossingUpperBound();
double load_delay = findVlCrossing(vth_, t_lower, t_upper); double load_delay = findVlCrossing(vth_, t_lower, t_upper);
@ -585,8 +579,7 @@ DmpAlg::loadDelaySlew(const Pin *,
} }
delay = delay1; delay = delay1;
slew = slew1; slew = slew1;
} } catch (DmpError &error) {
catch (DmpError &error) {
fail(error.what()); fail(error.what());
delay = elmore_; delay = elmore_;
slew = drvr_slew_; slew = drvr_slew_;
@ -600,17 +593,15 @@ DmpAlg::findVlCrossing(double vth,
double t_lower, double t_lower,
double t_upper) double t_upper)
{ {
FindRootFunc vl_func = [&] (double t, FindRootFunc vl_func = [&](double t, double &y, double &dy) {
double &y,
double &dy) {
double vl, vl_dt; double vl, vl_dt;
Vl(t, vl, vl_dt); Vl(t, vl, vl_dt);
y = vl - vth; y = vl - vth;
dy = vl_dt; dy = vl_dt;
}; };
bool fail; bool fail;
double t_vth = findRoot(vl_func, t_lower, t_upper, vth_time_tol, double t_vth =
find_root_max_iter, fail); findRoot(vl_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail);
if (fail) if (fail)
throw DmpError("find Vl crossing failed"); throw DmpError("find Vl crossing failed");
return t_vth; return t_vth;
@ -654,12 +645,12 @@ DmpAlg::Vl(double t,
void void
DmpAlg::showVl() DmpAlg::showVl()
{ {
report_->reportLine(" t vl(t)"); report_->report(" t vl(t)");
double ub = vlCrossingUpperBound(); double ub = vlCrossingUpperBound();
for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) { for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) {
double vl, dvl_dt; double vl, dvl_dt;
Vl(t, vl, dvl_dt); Vl(t, vl, dvl_dt);
report_->reportLine(" %g %g", t, vl); report_->report(" {:g} {:g}", t, vl);
} }
} }
@ -668,8 +659,7 @@ DmpAlg::fail(const char *reason)
{ {
// Report failures with a unique debug flag. // Report failures with a unique debug flag.
if (debug_->check("dmp_ceff", 1) || debug_->check("dcalc_error", 1)) 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", report_->report("delay_calc: DMP failed - {} c2={} rpi={} c1={} rd={}", reason,
reason,
units_->capacitanceUnit()->asString(c2_), units_->capacitanceUnit()->asString(c2_),
units_->resistanceUnit()->asString(rpi_), units_->resistanceUnit()->asString(rpi_),
units_->capacitanceUnit()->asString(c1_), units_->capacitanceUnit()->asString(c1_),
@ -694,14 +684,14 @@ public:
double c2, double c2,
double rpi, double rpi,
double c1) override; double c1) override;
void gateDelaySlew(// Return values. void gateDelaySlew( // Return values.
double &delay, double &delay,
double &slew) override; double &slew) override;
void loadDelaySlew(const Pin *, void loadDelaySlew(const Pin *,
double elmore, double elmore,
// Return values. // Return values.
ArcDelay &delay, double &delay,
Slew &slew) override; double &slew) override;
void evalDmpEqns() override; void evalDmpEqns() override;
double voCrossingUpperBound() override; double voCrossingUpperBound() override;
@ -716,8 +706,9 @@ private:
double &dvl_dt) override; double &dvl_dt) override;
}; };
DmpCap::DmpCap(StaState *sta): DmpCap::DmpCap(StaState *sta) :
DmpAlg(1, sta) DmpAlg(1,
sta)
{ {
} }
@ -734,17 +725,17 @@ DmpCap::init(const LibertyLibrary *drvr_library,
double c1) double c1)
{ {
debugPrint(debug_, "dmp_ceff", 3, "Using DMP cap"); debugPrint(debug_, "dmp_ceff", 3, "Using DMP cap");
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
rd, in_slew, c2, rpi, c1); c1);
ceff_ = c1 + c2; ceff_ = c1 + c2;
} }
void void
DmpCap::gateDelaySlew(// Return values. DmpCap::gateDelaySlew( // Return values.
double &delay, double &delay,
double &slew) double &slew)
{ {
debugPrint(debug_, "dmp_ceff", 3, " ceff = %s", debugPrint(debug_, "dmp_ceff", 3, " ceff = {}",
units_->capacitanceUnit()->asString(ceff_)); units_->capacitanceUnit()->asString(ceff_));
gateCapDelaySlew(ceff_, delay, slew); gateCapDelaySlew(ceff_, delay, slew);
drvr_slew_ = slew; drvr_slew_ = slew;
@ -753,8 +744,8 @@ DmpCap::gateDelaySlew(// Return values.
void void
DmpCap::loadDelaySlew(const Pin *, DmpCap::loadDelaySlew(const Pin *,
double elmore, double elmore,
ArcDelay &delay, double &delay,
Slew &slew) double &slew)
{ {
delay = elmore; delay = elmore;
slew = drvr_slew_; slew = drvr_slew_;
@ -782,7 +773,7 @@ DmpCap::voCrossingUpperBound()
} }
void void
DmpCap::Vl0(double , DmpCap::Vl0(double,
// Return values. // Return values.
double &vl, double &vl,
double &dvl_dt) double &dvl_dt)
@ -809,7 +800,7 @@ public:
double c2, double c2,
double rpi, double rpi,
double c1) override; double c1) override;
void gateDelaySlew(// Return values. void gateDelaySlew( // Return values.
double &delay, double &delay,
double &slew) override; double &slew) override;
void evalDmpEqns() override; void evalDmpEqns() override;
@ -847,7 +838,8 @@ private:
}; };
DmpPi::DmpPi(StaState *sta) : DmpPi::DmpPi(StaState *sta) :
DmpAlg(3, sta), DmpAlg(3,
sta),
p1_(0.0), p1_(0.0),
p2_(0.0), p2_(0.0),
z1_(0.0), z1_(0.0),
@ -875,8 +867,8 @@ DmpPi::init(const LibertyLibrary *drvr_library,
double c1) double c1)
{ {
debugPrint(debug_, "dmp_ceff", 3, "Using DMP Pi"); debugPrint(debug_, "dmp_ceff", 3, "Using DMP Pi");
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
in_slew, c2, rpi, c1); c1);
// Find poles/zeros. // Find poles/zeros.
z1_ = 1.0 / (rpi_ * c1_); z1_ = 1.0 / (rpi_ * c1_);
@ -900,7 +892,7 @@ DmpPi::init(const LibertyLibrary *drvr_library,
} }
void void
DmpPi::gateDelaySlew(// Return values. DmpPi::gateDelaySlew( // Return values.
double &delay, double &delay,
double &slew) double &slew)
{ {
@ -911,23 +903,21 @@ DmpPi::gateDelaySlew(// Return values.
double table_delay, table_slew; double table_delay, table_slew;
gateCapDelaySlew(ceff_, table_delay, table_slew); gateCapDelaySlew(ceff_, table_delay, table_slew);
delay = table_delay; delay = table_delay;
//slew = table_slew; // slew = table_slew;
try { try {
double vo_delay, vo_slew; double vo_delay, vo_slew;
findDriverDelaySlew(vo_delay, vo_slew); findDriverDelaySlew(vo_delay, vo_slew);
driver_valid_ = true; driver_valid_ = true;
// Save Vo delay to measure load wire delay waveform. // Save Vo delay to measure load wire delay waveform.
vo_delay_ = vo_delay; vo_delay_ = vo_delay;
//delay = vo_delay; // delay = vo_delay;
slew = vo_slew; slew = vo_slew;
} } catch (DmpError &error) {
catch (DmpError &error) {
fail(error.what()); fail(error.what());
// Fall back to table slew. // Fall back to table slew.
slew = table_slew; slew = table_slew;
} }
} } catch (DmpError &error) {
catch (DmpError &error) {
fail(error.what()); fail(error.what());
// Driver calculation failed - use Ceff=c1+c2. // Driver calculation failed - use Ceff=c1+c2.
ceff_ = c1_ + c2_; ceff_ = c1_ + c2_;
@ -941,8 +931,7 @@ DmpPi::findDriverParamsPi()
{ {
try { try {
findDriverParams(c2_ + c1_); findDriverParams(c2_ + c1_);
} } catch (DmpError &) {
catch (DmpError &) {
findDriverParams(c2_); findDriverParams(c2_);
} }
} }
@ -987,34 +976,31 @@ DmpPi::evalDmpEqns()
fjac_[DmpFunc::ipi][DmpParam::dt] = fjac_[DmpFunc::ipi][DmpParam::dt] =
(-A_ * dt + B_ * dt * exp_p1_dt - (2 * B_ / p1_) * (1.0 - exp_p1_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) + D_ * dt * exp_p2_dt - (2 * D_ / p2_) * (1.0 - exp_p2_dt)
+ rd_ * ceff * (dt + dt * exp_dt_rd_ceff + rd_ * ceff
- 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff))) * (dt + dt * exp_dt_rd_ceff - 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff)))
/ (rd_ * dt * dt * dt); / (rd_ * dt * dt * dt);
fjac_[DmpFunc::ipi][DmpParam::ceff] = fjac_[DmpFunc::ipi][DmpParam::ceff] =
(2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff))) (2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff)))
/ (dt * dt); / (dt * dt);
dy(t_vl, t0, dt, ceff, dy(t_vl, t0, dt, ceff, fjac_[DmpFunc::y20][DmpParam::t0],
fjac_[DmpFunc::y20][DmpParam::t0], fjac_[DmpFunc::y20][DmpParam::dt], fjac_[DmpFunc::y20][DmpParam::ceff]);
fjac_[DmpFunc::y20][DmpParam::dt],
fjac_[DmpFunc::y20][DmpParam::ceff]);
dy(t_vth, t0, dt, ceff, dy(t_vth, t0, dt, ceff, fjac_[DmpFunc::y50][DmpParam::t0],
fjac_[DmpFunc::y50][DmpParam::t0], fjac_[DmpFunc::y50][DmpParam::dt], fjac_[DmpFunc::y50][DmpParam::ceff]);
fjac_[DmpFunc::y50][DmpParam::dt],
fjac_[DmpFunc::y50][DmpParam::ceff]);
if (debug_->check("dmp_ceff", 4)) { if (debug_->check("dmp_ceff", 4)) {
showX(); showX();
showFvec(); showFvec();
showJacobian(); showJacobian();
report_->reportLine("................."); report_->report(".................");
} }
} }
// Eqn 13, Eqn 14. // Eqn 13, Eqn 14.
double double
DmpPi::ipiIceff(double, double dt, DmpPi::ipiIceff(double,
double dt,
double ceff_time, double ceff_time,
double ceff) double ceff)
{ {
@ -1024,8 +1010,8 @@ DmpPi::ipiIceff(double, double dt,
double ipi = (A_ * ceff_time + (B_ / p1_) * (1.0 - exp_p1_dt) double ipi = (A_ * ceff_time + (B_ / p1_) * (1.0 - exp_p1_dt)
+ (D_ / p2_) * (1.0 - exp_p2_dt)) + (D_ / p2_) * (1.0 - exp_p2_dt))
/ (rd_ * ceff_time * dt); / (rd_ * ceff_time * dt);
double iceff = (rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff) double iceff =
* (1.0 - exp_dt_rd_ceff)) (rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff) * (1.0 - exp_dt_rd_ceff))
/ (rd_ * ceff_time * dt); / (rd_ * ceff_time * dt);
return ipi - iceff; return ipi - iceff;
} }
@ -1051,14 +1037,13 @@ DmpPi::Vl0(double t,
double D1 = k0_ * (k1_ - k2_ / p3_); double D1 = k0_ * (k1_ - k2_ / p3_);
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_); double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
double D4 = -p3_ * k0_ * k4_ / (p2_ - p3_); double D4 = -p3_ * k0_ * k4_ / (p2_ - p3_);
double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_) double D5 =
+ p3_ * k4_ / (p2_ - p3_)); k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_) + p3_ * k4_ / (p2_ - p3_));
double exp_p1 = exp2(-p1_ * t); double exp_p1 = exp2(-p1_ * t);
double exp_p2 = exp2(-p2_ * t); double exp_p2 = exp2(-p2_ * t);
double exp_p3 = exp2(-p3_ * t); double exp_p3 = exp2(-p3_ * t);
vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3; vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3;
dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 - D5 * p3_ * exp_p3;
- D5 * p3_ * exp_p3;
} }
double double
@ -1080,7 +1065,8 @@ public:
}; };
DmpOnePole::DmpOnePole(StaState *sta) : DmpOnePole::DmpOnePole(StaState *sta) :
DmpAlg(2, sta) DmpAlg(2,
sta)
{ {
} }
@ -1104,19 +1090,15 @@ DmpOnePole::evalDmpEqns()
showFvec(); showFvec();
} }
dy(t_vl, t0, dt, ceff_, dy(t_vl, t0, dt, ceff_, fjac_[DmpFunc::y20][DmpParam::t0],
fjac_[DmpFunc::y20][DmpParam::t0], fjac_[DmpFunc::y20][DmpParam::dt], ignore2);
fjac_[DmpFunc::y20][DmpParam::dt],
ignore2);
dy(t_vth, t0, dt, ceff_, dy(t_vth, t0, dt, ceff_, fjac_[DmpFunc::y50][DmpParam::t0],
fjac_[DmpFunc::y50][DmpParam::t0], fjac_[DmpFunc::y50][DmpParam::dt], ignore2);
fjac_[DmpFunc::y50][DmpParam::dt],
ignore2);
if (debug_->check("dmp_ceff", 4)) { if (debug_->check("dmp_ceff", 4)) {
showJacobian(); showJacobian();
report_->reportLine("................."); report_->report(".................");
} }
} }
@ -1144,7 +1126,7 @@ public:
double c2, double c2,
double rpi, double rpi,
double c1) override; double c1) override;
void gateDelaySlew(// Return values. void gateDelaySlew( // Return values.
double &delay, double &delay,
double &slew) override; double &slew) override;
@ -1193,8 +1175,8 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library,
double c1) double c1)
{ {
debugPrint(debug_, "dmp_ceff", 3, "Using DMP C2=0"); debugPrint(debug_, "dmp_ceff", 3, "Using DMP C2=0");
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
in_slew, c2, rpi, c1); c1);
ceff_ = c1; ceff_ = c1;
z1_ = 1.0 / (rpi_ * c1_); z1_ = 1.0 / (rpi_ * c1_);
@ -1217,8 +1199,7 @@ DmpZeroC2::gateDelaySlew(// Return values.
findDriverDelaySlew(delay, slew); findDriverDelaySlew(delay, slew);
driver_valid_ = true; driver_valid_ = true;
vo_delay_ = delay; vo_delay_ = delay;
} } catch (DmpError &error) {
catch (DmpError &error) {
fail(error.what()); fail(error.what());
// Fall back to table slew. // Fall back to table slew.
driver_valid_ = false; driver_valid_ = false;
@ -1271,7 +1252,7 @@ newtonRaphson(const int max_iter,
double x[], double x[],
const int size, const int size,
const double x_tol, const double x_tol,
std::function<void ()> eval, std::function<void()> eval,
// Temporaries supplied by caller. // Temporaries supplied by caller.
double *fvec, double *fvec,
double **fjac, double **fjac,
@ -1498,21 +1479,36 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
parasitics_->piModel(parasitic, c2, rpi, c1); parasitics_->piModel(parasitic, c2, rpi, c1);
if (std::isnan(c2) || std::isnan(c1) || std::isnan(rpi)) if (std::isnan(c2) || std::isnan(c1) || std::isnan(rpi))
report_->error(1040, "parasitic Pi model has NaNs."); 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); table_model, rf, in_slew1, c2, rpi, c1);
double gate_delay, drvr_slew; double gate_delay, drvr_slew;
gateDelaySlew(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()); ArcDcalcResult dcalc_result(load_pin_index_map.size());
dcalc_result.setGateDelay(gate_delay); dcalc_result.setGateDelay(gate_delay2);
dcalc_result.setDrvrSlew(drvr_slew); dcalc_result.setDrvrSlew(drvr_slew2);
for (const auto &[load_pin, load_idx] : load_pin_index_map) { for (const auto &[load_pin, load_idx] : load_pin_index_map) {
ArcDelay wire_delay; double wire_delay;
Slew load_slew; double load_slew;
loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic, loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic,
wire_delay, load_slew); wire_delay, load_slew);
dcalc_result.setWireDelay(load_idx, wire_delay); // Copy pocv params from driver.
dcalc_result.setLoadSlew(load_idx, load_slew); 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; return dcalc_result;
} }
@ -1520,10 +1516,11 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
ArcDcalcResult dcalc_result = ArcDcalcResult dcalc_result =
LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
load_pin_index_map, scene, min_max); load_pin_index_map, scene, min_max);
if (parasitic if (parasitic && !unsuppored_model_warned_) {
&& !unsuppored_model_warned_) {
unsuppored_model_warned_ = true; 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()); drvr_cell->name());
} }
return dcalc_result; return dcalc_result;
@ -1543,8 +1540,7 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library,
{ {
double rd = 0.0; double rd = 0.0;
if (gate_model) { if (gate_model) {
rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, pvt);
pvt, variables_->pocvEnabled());
// Zero Rd means the table is constant and thus independent of load cap. // Zero Rd means the table is constant and thus independent of load cap.
if (rd < 1e-2 if (rd < 1e-2
// Rpi is small compared to Rd, which makes the load capacitive. // Rpi is small compared to Rd, which makes the load capacitive.
@ -1560,16 +1556,15 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library,
} }
else else
dmp_alg_ = dmp_cap_; dmp_alg_ = dmp_cap_;
dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
rf, rd, in_slew, c2, rpi, c1); c1);
debugPrint(debug_, "dmp_ceff", 3, 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_->timeUnit()->asString(in_slew),
units_->capacitanceUnit()->asString(c2), units_->capacitanceUnit()->asString(c2),
units_->resistanceUnit()->asString(rpi), units_->resistanceUnit()->asString(rpi),
units_->capacitanceUnit()->asString(c1), units_->capacitanceUnit()->asString(c1),
units_->resistanceUnit()->asString(rd), units_->resistanceUnit()->asString(rd), dmp_alg_->name());
dmp_alg_->name());
} }
std::string std::string
@ -1583,8 +1578,9 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
const MinMax *min_max, const MinMax *min_max,
int digits) int digits)
{ {
ArcDcalcResult dcalc_result = gateDelay(drvr_pin, arc, in_slew, load_cap, ArcDcalcResult dcalc_result =
parasitic, load_pin_index_map, scene, min_max); gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map,
scene, min_max);
GateTableModel *model = arc->gateTableModel(scene, min_max); GateTableModel *model = arc->gateTableModel(scene, min_max);
float c_eff = 0.0; float c_eff = 0.0;
std::string result; std::string result;
@ -1612,14 +1608,12 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
else else
c_eff = load_cap; c_eff = load_cap;
if (model) { if (model) {
const Unit *time_unit = units->timeUnit();
float in_slew1 = delayAsFloat(in_slew); float in_slew1 = delayAsFloat(in_slew);
result += model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), result += model->reportGateDelay(pinPvt(drvr_pin, scene, min_max),
in_slew1, c_eff, in_slew1, c_eff, min_max,
variables_->pocvEnabled(), digits); variables_->pocvMode(), digits);
result += "Driver waveform slew = "; result += "Driver waveform slew = ";
float drvr_slew = delayAsFloat(dcalc_result.drvrSlew()); result += delayAsString(dcalc_result.drvrSlew(), min_max, digits, this);
result += time_unit->asString(drvr_slew, digits);
result += '\n'; result += '\n';
} }
return result; return result;
@ -1632,23 +1626,20 @@ gateModelRd(const LibertyCell *cell,
double in_slew, double in_slew,
double c2, double c2,
double c1, double c1,
const Pvt *pvt, const Pvt *pvt)
bool pocv_enabled)
{ {
float cap1 = c1 + c2; float cap1 = c1 + c2;
float cap2 = cap1 + 1e-15; float cap2 = cap1 + 1e-15;
ArcDelay d1, d2; float d1, d2, s1, s2;
Slew s1, s2; gate_model->gateDelay(pvt, in_slew, cap1, d1, s1);
gate_model->gateDelay(pvt, in_slew, cap1, pocv_enabled, d1, s1); gate_model->gateDelay(pvt, in_slew, cap2, d2, s2);
gate_model->gateDelay(pvt, in_slew, cap2, pocv_enabled, d2, s2);
double vth = cell->libertyLibrary()->outputThreshold(rf); double vth = cell->libertyLibrary()->outputThreshold(rf);
float rd = -std::log(vth) * std::abs(delayAsFloat(d1) - delayAsFloat(d2)) float rd = -std::log(vth) * std::abs(d1 - d2) / (cap2 - cap1);
/ (cap2 - cap1);
return rd; return rd;
} }
void void
DmpCeffDelayCalc::gateDelaySlew(// Return values. DmpCeffDelayCalc::gateDelaySlew( // Return values.
double &delay, double &delay,
double &slew) double &slew)
{ {
@ -1658,8 +1649,8 @@ DmpCeffDelayCalc::gateDelaySlew(// Return values.
void void
DmpCeffDelayCalc::loadDelaySlewElmore(const Pin *load_pin, DmpCeffDelayCalc::loadDelaySlewElmore(const Pin *load_pin,
double elmore, double elmore,
ArcDelay &delay, double &delay,
Slew &slew) double &slew)
{ {
if (dmp_alg_) if (dmp_alg_)
dmp_alg_->loadDelaySlew(load_pin, elmore, delay, slew); dmp_alg_->loadDelaySlew(load_pin, elmore, delay, slew);
@ -1678,7 +1669,7 @@ DmpCeffDelayCalc::copyState(const StaState *sta)
DmpError::DmpError(const char *what) : DmpError::DmpError(const char *what) :
what_(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. // 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 LibertyLibrary *drvr_library,
const Parasitic *parasitic, const Parasitic *parasitic,
// Return values. // Return values.
ArcDelay &wire_delay, double &wire_delay,
Slew &load_slew) = 0; double &load_slew) = 0;
void gateDelaySlew(// Return values. void gateDelaySlew(// Return values.
double &delay, double &delay,
double &slew); double &slew);
void loadDelaySlewElmore(const Pin *load_pin, void loadDelaySlewElmore(const Pin *load_pin,
double elmore, double elmore,
ArcDelay &delay, double &delay,
Slew &slew); double &slew);
// Select the appropriate special case Dartu/Menezes/Pileggi algorithm. // Select the appropriate special case Dartu/Menezes/Pileggi algorithm.
void setCeffAlgorithm(const LibertyLibrary *library, void setCeffAlgorithm(const LibertyLibrary *library,
const LibertyCell *cell, const LibertyCell *cell,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,15 +38,16 @@
#include "Parasitics.hh" #include "Parasitics.hh"
#include "GraphDelayCalc.hh" #include "GraphDelayCalc.hh"
#include "DmpDelayCalc.hh" #include "DmpDelayCalc.hh"
#include "Format.hh"
#include <Eigen/LU> #include <Eigen/LU>
#include <Eigen/QR> #include <Eigen/QR>
namespace sta { namespace sta {
using Eigen::SparseLU;
using Eigen::HouseholderQR;
using Eigen::ColPivHouseholderQR; using Eigen::ColPivHouseholderQR;
using Eigen::HouseholderQR;
using Eigen::SparseLU;
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998 // Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
// McGraw-Hill, Inc. New York, NY. // McGraw-Hill, Inc. New York, NY.
@ -90,10 +91,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) :
{ {
} }
PrimaDelayCalc::~PrimaDelayCalc() PrimaDelayCalc::~PrimaDelayCalc() { delete table_dcalc_; }
{
delete table_dcalc_;
}
ArcDelayCalc * ArcDelayCalc *
PrimaDelayCalc::copy() PrimaDelayCalc::copy()
@ -130,8 +128,8 @@ PrimaDelayCalc::findParasitic(const Pin *drvr_pin,
bool has_wire_cap; bool has_wire_cap;
graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap, graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap,
fanout, has_wire_cap); fanout, has_wire_cap);
parasitic = parasitics->makeWireloadNetwork(drvr_pin, wireload, parasitic =
fanout, scene, min_max); parasitics->makeWireloadNetwork(drvr_pin, wireload, fanout, scene, min_max);
} }
return parasitic; return parasitic;
} }
@ -160,14 +158,14 @@ PrimaDelayCalc::inputPortDelay(const Pin *drvr_pin,
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary(); LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
const Parasitic *pi_elmore = nullptr; const Parasitic *pi_elmore = nullptr;
if (parasitic && parasitics->isParasiticNetwork(parasitic)) if (parasitic && parasitics->isParasiticNetwork(parasitic))
pi_elmore = parasitics->reduceToPiElmore(parasitic, drvr_pin, rf, pi_elmore =
scene, min_max); parasitics->reduceToPiElmore(parasitic, drvr_pin, rf, scene, min_max);
for (auto load_pin_index : load_pin_index_map) { for (auto load_pin_index : load_pin_index_map) {
const Pin *load_pin = load_pin_index.first; const Pin *load_pin = load_pin_index.first;
size_t load_idx = load_pin_index.second; size_t load_idx = load_pin_index.second;
ArcDelay wire_delay = 0.0; double wire_delay = 0.0;
Slew load_slew = in_slew; double load_slew = in_slew;
bool elmore_exists = false; bool elmore_exists = false;
float elmore = 0.0; float elmore = 0.0;
if (pi_elmore) if (pi_elmore)
@ -193,9 +191,10 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin,
const MinMax *min_max) const MinMax *min_max)
{ {
ArcDcalcArgSeq dcalc_args; ArcDcalcArgSeq dcalc_args;
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic); dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap,
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map, parasitic);
scene, min_max); ArcDcalcResultSeq dcalc_results =
gateDelays(dcalc_args, load_pin_index_map, scene, min_max);
return dcalc_results[0]; return dcalc_results[0];
} }
@ -229,15 +228,15 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
&& output_waveforms->slewAxis()->inBounds(in_slew) && output_waveforms->slewAxis()->inBounds(in_slew)
&& output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) { && output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) {
output_waveforms_[drvr_idx] = output_waveforms; output_waveforms_[drvr_idx] = output_waveforms;
debugPrint(debug_, "ccs_dcalc", 1, "%s %s", debugPrint(debug_, "ccs_dcalc", 1, "{} {}", dcalc_arg.drvrCell()->name(),
dcalc_arg.drvrCell()->name(),
drvr_rf_->shortName()); drvr_rf_->shortName());
LibertyCell *drvr_cell = dcalc_arg.drvrCell(); LibertyCell *drvr_cell = dcalc_arg.drvrCell();
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary(); const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
bool vdd_exists; bool vdd_exists;
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists); drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
if (!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_); drvr_cell->ensureVoltageWaveforms(scenes_);
if (drvr_idx == 0) { if (drvr_idx == 0) {
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_; vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
@ -268,13 +267,13 @@ PrimaDelayCalc::tableDcalcResults()
const Pin *drvr_pin = dcalc_arg.drvrPin(); const Pin *drvr_pin = dcalc_arg.drvrPin();
if (drvr_pin) { if (drvr_pin) {
const RiseFall *rf = dcalc_arg.drvrEdge(); const RiseFall *rf = dcalc_arg.drvrEdge();
const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf, const Parasitic *parasitic =
scene_, min_max_); table_dcalc_->findParasitic(drvr_pin, rf, scene_, min_max_);
dcalc_arg.setParasitic(parasitic); dcalc_arg.setParasitic(parasitic);
} }
} }
return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, scene_,
scene_, min_max_); min_max_);
} }
void void
@ -284,8 +283,7 @@ PrimaDelayCalc::simulate()
stampEqns(); stampEqns();
setXinit(); setXinit();
if (prima_order_ > 0 if (prima_order_ > 0 && node_count_ > prima_order_) {
&& node_count_ > prima_order_) {
primaReduce(); primaReduce();
simulate1(Gq_, Cq_, Bq_, xq_init_, Vq_, prima_order_); simulate1(Gq_, Cq_, Bq_, xq_init_, Vq_, prima_order_);
} }
@ -315,7 +313,8 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
v_ = v_prev_ = x_to_v * x_init; v_ = v_prev_ = x_to_v * x_init;
time_step_ = time_step_prev_ = timeStep(); 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); MatrixSd A(order, order);
A = G + (2.0 / time_step_) * C; A = G + (2.0 / time_step_) * C;
@ -336,8 +335,8 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
v_ = v_prev_ = x_to_v * x_init; v_ = v_prev_ = x_to_v * x_init;
// voltageTime is always for a rising waveform so 0.0v is initial voltage. // voltageTime is always for a rising waveform so 0.0v is initial voltage.
double time_begin = output_waveforms_[0]->voltageTime((*dcalc_args_)[0].inSlewFlt(), double time_begin = output_waveforms_[0]->voltageTime(
ceff_[0], 0.0); (*dcalc_args_)[0].inSlewFlt(), ceff_[0], 0.0);
// Limit in case load voltage waveforms don't get to final value. // Limit in case load voltage waveforms don't get to final value.
double time_end = time_begin + maxTime(); double time_end = time_begin + maxTime();
@ -351,7 +350,7 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
v_ = x_to_v * x; v_ = x_to_v * x;
const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[0]; 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), delayAsString(time, this),
units_->capacitanceUnit()->asString(ceff_[0]), units_->capacitanceUnit()->asString(ceff_[0]),
voltage(dcalc_arg.drvrPin()), voltage(dcalc_arg.drvrPin()),
@ -429,9 +428,8 @@ PrimaDelayCalc::findNodeCount()
const Pin *pin = parasitics_->pin(node); const Pin *pin = parasitics_->pin(node);
if (pin) { if (pin) {
pin_node_map_[pin] = node_idx; pin_node_map_[pin] = node_idx;
debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu", debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}",
network_->pathName(pin), network_->pathName(pin), node_idx);
node_idx);
} }
double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node); double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
node_capacitances_.push_back(cap); node_capacitances_.push_back(cap);
@ -441,14 +439,12 @@ PrimaDelayCalc::findNodeCount()
for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) { for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) {
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_; float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
ParasiticNode *node1 = parasitics_->node1(capacitor); ParasiticNode *node1 = parasitics_->node1(capacitor);
if (node1 if (node1 && !parasitics_->isExternal(node1)) {
&& !parasitics_->isExternal(node1)) {
size_t node_idx = node_index_map_[node1]; size_t node_idx = node_index_map_[node1];
node_capacitances_[node_idx] += cap; node_capacitances_[node_idx] += cap;
} }
ParasiticNode *node2 = parasitics_->node2(capacitor); ParasiticNode *node2 = parasitics_->node2(capacitor);
if (node2 if (node2 && !parasitics_->isExternal(node2)) {
&& !parasitics_->isExternal(node2)) {
size_t node_idx = node_index_map_[node2]; size_t node_idx = node_index_map_[node2];
node_capacitances_[node_idx] += cap; node_capacitances_[node_idx] += cap;
} }
@ -496,9 +492,8 @@ PrimaDelayCalc::initCeffIdrvr()
const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx]; const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx];
ceff_[drvr_idx] = load_cap_; ceff_[drvr_idx] = load_cap_;
// voltageTime is always for a rising waveform so 0.0v is initial voltage. // voltageTime is always for a rising waveform so 0.0v is initial voltage.
drvr_current_[drvr_idx] = drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), dcalc_arg.inSlewFlt(), ceff_[drvr_idx], 0.0);
ceff_[drvr_idx], 0.0);
} }
} }
@ -617,8 +612,7 @@ PrimaDelayCalc::updateCeffIdrvr()
double v2 = voltagePrev(node_idx); double v2 = voltagePrev(node_idx);
double dv = v1 - v2; double dv = v1 - v2;
if (drvr_rf_ == RiseFall::rise()) { if (drvr_rf_ == RiseFall::rise()) {
if (drvr_current != 0.0 if (drvr_current != 0.0 && dv > 0.0) {
&& dv > 0.0) {
double ceff = drvr_current * time_step_ / dv; double ceff = drvr_current * time_step_ / dv;
if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff))
ceff_[drvr_idx] = ceff; ceff_[drvr_idx] = ceff;
@ -627,13 +621,11 @@ PrimaDelayCalc::updateCeffIdrvr()
// Whoa partner. Head'n for the weeds. // Whoa partner. Head'n for the weeds.
drvr_current_[drvr_idx] = 0.0; drvr_current_[drvr_idx] = 0.0;
else else
drvr_current_[drvr_idx] = drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), dcalc_arg.inSlewFlt(), ceff_[drvr_idx], v1);
ceff_[drvr_idx], v1);
} }
else { else {
if (drvr_current != 0.0 if (drvr_current != 0.0 && dv < 0.0) {
&& dv < 0.0) {
double ceff = drvr_current * time_step_ / dv; double ceff = drvr_current * time_step_ / dv;
if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff)) if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff))
ceff_[drvr_idx] = ceff; ceff_[drvr_idx] = ceff;
@ -643,10 +635,8 @@ PrimaDelayCalc::updateCeffIdrvr()
drvr_current_[drvr_idx] = 0.0; drvr_current_[drvr_idx] = 0.0;
} }
else else
drvr_current_[drvr_idx] = drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(), dcalc_arg.inSlewFlt(), ceff_[drvr_idx], vdd_ - v1);
ceff_[drvr_idx],
vdd_ - v1);
} }
} }
} }
@ -657,10 +647,8 @@ PrimaDelayCalc::loadWaveformsFinished()
for (auto pin_node : pin_node_map_) { for (auto pin_node : pin_node_map_) {
size_t node_idx = pin_node.second; size_t node_idx = pin_node.second;
double v = voltage(node_idx); double v = voltage(node_idx);
if ((drvr_rf_ == RiseFall::rise() if ((drvr_rf_ == RiseFall::rise() && v < vh_ + (vdd_ - vh_) * .5)
&& v < vh_ + (vdd_ - vh_) * .5) || (drvr_rf_ == RiseFall::fall() && (v > vl_ * .5))) {
|| (drvr_rf_ == RiseFall::fall()
&& (v > vl_ * .5))) {
return false; return false;
} }
} }
@ -678,12 +666,10 @@ PrimaDelayCalc::measureThresholds(double time)
double v_prev = voltagePrev(node_idx); double v_prev = voltagePrev(node_idx);
for (size_t m = 0; m < measure_threshold_count_; m++) { for (size_t m = 0; m < measure_threshold_count_; m++) {
double th = measure_thresholds_[m]; double th = measure_thresholds_[m];
if ((v_prev < th && th <= v) if ((v_prev < th && th <= v) || (v_prev > th && th >= v)) {
|| (v_prev > th && th >= v)) { double t_cross =
double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev); time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s", debugPrint(debug_, "ccs_measure", 1, "node {} cross {:.2f} {}", node_idx, th,
node_idx,
th,
delayAsString(t_cross, this)); delayAsString(t_cross, this));
threshold_times_[node_idx][m] = t_cross; threshold_times_[node_idx][m] = t_cross;
} }
@ -722,14 +708,12 @@ PrimaDelayCalc::dcalcResults()
size_t drvr_node = pin_node_map_[drvr_pin]; size_t drvr_node = pin_node_map_[drvr_pin];
ThresholdTimes &drvr_times = threshold_times_[drvr_node]; ThresholdTimes &drvr_times = threshold_times_[drvr_node];
float ref_time = output_waveforms_[drvr_idx]->referenceTime(dcalc_arg.inSlewFlt()); float ref_time = output_waveforms_[drvr_idx]->referenceTime(dcalc_arg.inSlewFlt());
ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time; double gate_delay = drvr_times[threshold_vth] - ref_time;
Slew drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]); double drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]);
dcalc_result.setGateDelay(gate_delay); dcalc_result.setGateDelay(gate_delay);
dcalc_result.setDrvrSlew(drvr_slew); dcalc_result.setDrvrSlew(drvr_slew);
debugPrint(debug_, "ccs_dcalc", 2, debugPrint(debug_, "ccs_dcalc", 2, "{} gate delay {} slew {}",
"%s gate delay %s slew %s", network_->pathName(drvr_pin), delayAsString(gate_delay, this),
network_->pathName(drvr_pin),
delayAsString(gate_delay, this),
delayAsString(drvr_slew, this)); delayAsString(drvr_slew, this));
dcalc_result.setLoadCount(load_pin_index_map_->size()); dcalc_result.setLoadCount(load_pin_index_map_->size());
@ -739,10 +723,9 @@ PrimaDelayCalc::dcalcResults()
size_t load_node = pin_node_map_[load_pin]; size_t load_node = pin_node_map_[load_pin];
ThresholdTimes &wire_times = threshold_times_[load_node]; ThresholdTimes &wire_times = threshold_times_[load_node];
ThresholdTimes &drvr_times = threshold_times_[drvr_node]; ThresholdTimes &drvr_times = threshold_times_[drvr_node];
ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth]; double wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
Slew load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]); double load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
debugPrint(debug_, "ccs_dcalc", 2, debugPrint(debug_, "ccs_dcalc", 2, "load {} {} delay {} slew {}",
"load %s %s delay %s slew %s",
network_->pathName(load_pin), network_->pathName(load_pin),
drvr_rf_->shortName(), drvr_rf_->shortName(),
delayAsString(wire_delay, this), delayAsString(wire_delay, this),
@ -849,10 +832,12 @@ PrimaDelayCalc::primaReduce2()
// Modified Gram-Schmidt orthonormalization // Modified Gram-Schmidt orthonormalization
for (size_t j = 0; j < k; j++) { for (size_t j = 0; j < k; j++) {
Eigen::MatrixXd H = Vq.block(0, j * port_count_, order_, port_count_).transpose() 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, 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::MatrixXd Vq_k = Vq.block(0, k * port_count_, order_, port_count_);
Eigen::HouseholderQR<Eigen::MatrixXd> Vq_k_solver(Vq_k); Eigen::HouseholderQR<Eigen::MatrixXd> Vq_k_solver(Vq_k);
@ -920,7 +905,8 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin,
if (model) { if (model) {
float in_slew1 = delayAsFloat(in_slew); float in_slew1 = delayAsFloat(in_slew);
return model->reportGateDelay(pinPvt(drvr_pin, scene, min_max), 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 ""; return "";
} }
@ -956,8 +942,8 @@ Waveform
PrimaDelayCalc::watchWaveform(const Pin *pin) PrimaDelayCalc::watchWaveform(const Pin *pin)
{ {
FloatSeq &voltages = watch_pin_values_[pin]; FloatSeq &voltages = watch_pin_values_[pin];
TableAxisPtr time_axis = std::make_shared<TableAxis>(TableAxisVariable::time, TableAxisPtr time_axis =
FloatSeq(times_)); std::make_shared<TableAxis>(TableAxisVariable::time, FloatSeq(times_));
Table waveform(new FloatSeq(voltages), time_axis); Table waveform(new FloatSeq(voltages), time_axis);
return waveform; return waveform;
} }
@ -968,7 +954,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name, PrimaDelayCalc::reportMatrix(const char *name,
MatrixSd &matrix) MatrixSd &matrix)
{ {
report_->reportLine("%s", name); report_->report("{}", name);
reportMatrix(matrix); reportMatrix(matrix);
} }
@ -976,7 +962,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name, PrimaDelayCalc::reportMatrix(const char *name,
Eigen::MatrixXd &matrix) Eigen::MatrixXd &matrix)
{ {
report_->reportLine("%s", name); report_->report("{}", name);
reportMatrix(matrix); reportMatrix(matrix);
} }
@ -984,7 +970,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name, PrimaDelayCalc::reportMatrix(const char *name,
Eigen::VectorXd &matrix) Eigen::VectorXd &matrix)
{ {
report_->reportLine("%s", name); report_->report("{}", name);
reportMatrix(matrix); reportMatrix(matrix);
} }
@ -992,7 +978,7 @@ void
PrimaDelayCalc::reportVector(const char *name, PrimaDelayCalc::reportVector(const char *name,
std::vector<double> &matrix) std::vector<double> &matrix)
{ {
report_->reportLine("%s", name); report_->report("{}", name);
reportVector(matrix); reportVector(matrix);
} }
@ -1001,13 +987,10 @@ PrimaDelayCalc::reportMatrix(MatrixSd &matrix)
{ {
for (Eigen::Index i = 0; i < matrix.rows(); i++) { for (Eigen::Index i = 0; i < matrix.rows(); i++) {
std::string line = "| "; std::string line = "| ";
for (Eigen::Index j = 0; j < matrix.cols(); j++) { for (Eigen::Index j = 0; j < matrix.cols(); j++)
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " ";
line += entry;
line += " ";
}
line += "|"; 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++) { for (Eigen::Index i = 0; i < matrix.rows(); i++) {
std::string line = "| "; std::string line = "| ";
for (Eigen::Index j = 0; j < matrix.cols(); j++) { for (Eigen::Index j = 0; j < matrix.cols(); j++)
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j)); line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " ";
line += entry;
line += " ";
}
line += "|"; line += "|";
report_->reportLineString(line); report_->reportLine(line);
} }
} }
@ -1031,25 +1011,21 @@ PrimaDelayCalc::reportMatrix(Eigen::VectorXd &matrix)
{ {
std::string line = "| "; std::string line = "| ";
for (Eigen::Index i = 0; i < matrix.rows(); i++) { for (Eigen::Index i = 0; i < matrix.rows(); i++) {
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i)); std::string entry =
line += entry; line += sta::format("{:10.3e}", matrix.coeff(i)) + " ";
line += " ";
} }
line += "|"; line += "|";
report_->reportLineString(line); report_->reportLine(line);
} }
void void
PrimaDelayCalc::reportVector(std::vector<double> &matrix) PrimaDelayCalc::reportVector(std::vector<double> &matrix)
{ {
std::string line = "| "; std::string line = "| ";
for (size_t i = 0; i < matrix.size(); i++) { for (size_t i = 0; i < matrix.size(); i++)
std::string entry = stdstrPrint("%10.3e", matrix[i]); line += sta::format("{:10.3e}", matrix[i]) + " ";
line += entry;
line += " ";
}
line += "|"; 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. 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 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 the std containers. The member functions are now templated functions found
in ContainerHelpers.hh. in ContainerHelpers.hh.
The Graph slew_rf_count option is no longer supported.
Release 2.6.2 2025/03/30 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. 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 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 leaf instance timing models have corresponding edges in the graph
between pins on the instance. 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 SDC
--- ---

View File

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

View File

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

View File

@ -26,51 +26,64 @@
namespace eval sta { 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 } { 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 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)] } { if { [info exists keys(-from)] && [info exists keys(-to)] } {
set from_pin [get_port_pin_error "from_pin" $keys(-from)] set from_pin [get_port_pin_error "from_pin" $keys(-from)]
set to_pin [get_port_pin_error "to_pin" $keys(-to)] set to_pin [get_port_pin_error "to_pin" $keys(-to)]
foreach from_vertex [$from_pin vertices] { foreach from_vertex [$from_pin vertices] {
foreach to_vertex [$to_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)] { } elseif [info exists keys(-from)] {
set from_pin [get_port_pin_error "from_pin" $keys(-from)] set from_pin [get_port_pin_error "from_pin" $keys(-from)]
foreach from_vertex [$from_pin vertices] { foreach from_vertex [$from_pin vertices] {
report_edges_ $from_vertex out_edge_iterator \ 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)] { } elseif [info exists keys(-to)] {
set to_pin [get_port_pin_error "to_pin" $keys(-to)] set to_pin [get_port_pin_error "to_pin" $keys(-to)]
foreach to_vertex [$to_pin vertices] { foreach to_vertex [$to_pin vertices] {
report_edges_ $to_vertex in_edge_iterator \ 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] set iter [$from_vertex out_edge_iterator]
while {[$iter has_next]} { while {[$iter has_next]} {
set edge [$iter next] set edge [$iter next]
if { [$edge to] == $to_vertex } { if { [$edge to] == $to_vertex } {
if { [$edge role] == "wire" } { 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 { } else {
report_edge_ $edge vertex_port_name vertex_port_name report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance
} }
} }
} }
$iter finish $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. # First report edges internal to the device.
set device_header 0 set device_header 0
set iter [$vertex $iter_proc] 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 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 $iter finish
@ -94,15 +107,14 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } {
while {[$iter has_next]} { while {[$iter has_next]} {
set edge [$iter next] set edge [$iter next]
if { [$edge role] == "wire" } { 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 $iter finish
} }
proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } { proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc \
global sta_report_default_digits digits report_variance } {
set latch_enable [$edge latch_d_to_q_en] set latch_enable [$edge latch_d_to_q_en]
if { $latch_enable != "" } { if { $latch_enable != "" } {
set latch_enable " enable $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] { 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 delays_fmt [format_delays $delays]
set disable_reason "" set disable_reason ""
if { [timing_arc_disabled $edge $arc] } { 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. # Separate delay list elements with colons.
proc format_delays { values } { proc format_delays { values } {
set result "" set result ""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,27 +24,336 @@
#pragma once #pragma once
#include "StaConfig.hh" #include <array>
#include <cstddef>
// IWYU pragma: begin_exports #include "StaConfig.hh"
#if (SSTA == 1) #include "MinMax.hh"
// 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
namespace sta { 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 ArcDelay = Delay;
using Slew = Delay; using Slew = Delay;
using Arrival = Delay; using Arrival = Delay;
using Required = Delay; using Required = Delay;
using Slack = 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 } // 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 #pragma once
#include <exception> #include <exception>
#include <string>
#include "Report.hh" #include "Report.hh"
@ -42,7 +43,7 @@ public:
class ExceptionMsg : public Exception class ExceptionMsg : public Exception
{ {
public: public:
ExceptionMsg(const char *msg, ExceptionMsg(const std::string &msg,
const bool suppressed); const bool suppressed);
virtual const char *what() const noexcept; virtual const char *what() const noexcept;
virtual bool suppressed() const { return suppressed_; } virtual bool suppressed() const { return suppressed_; }
@ -55,11 +56,11 @@ private:
class ExceptionLine : public Exception class ExceptionLine : public Exception
{ {
public: public:
ExceptionLine(const char *filename, ExceptionLine(const std::string &filename,
int line); int line);
protected: protected:
const char *filename_; std::string filename_;
int line_; int line_;
}; };
@ -67,29 +68,31 @@ protected:
class FileNotReadable : public Exception class FileNotReadable : public Exception
{ {
public: public:
FileNotReadable(const char *filename); FileNotReadable(std::string filename);
virtual const char *what() const noexcept; virtual const char *what() const noexcept;
protected: protected:
const char *filename_; std::string filename_;
std::string msg_;
}; };
// Failure opening filename for writing. // Failure opening filename for writing.
class FileNotWritable : public Exception class FileNotWritable : public Exception
{ {
public: public:
FileNotWritable(const char *filename); FileNotWritable(std::string filename);
virtual const char *what() const noexcept; virtual const char *what() const noexcept;
protected: protected:
const char *filename_; std::string filename_;
std::string msg_;
}; };
// Report an error condition that should not be possible. // Report an error condition that should not be possible.
// The default handler prints msg to stderr and exits. // The default handler prints msg to stderr and exits.
// The msg should NOT include a period or return. // The msg should NOT include a period or return.
// Only for use in those cases where a Report object is not available. // Only for use in those cases where a Report object is not available.
#define criticalError(id,msg) \ #define criticalError(id, msg) \
Report::defaultReport()->fileCritical(id, __FILE__, __LINE__, msg) Report::defaultReport()->fileCritical(id, __FILE__, __LINE__, msg)
} // namespace } // namespace

View File

@ -24,6 +24,7 @@
#pragma once #pragma once
#include <string>
#include <vector> #include <vector>
#include "Error.hh" #include "Error.hh"
@ -67,7 +68,7 @@ public:
virtual bool isGroupPath() const { return false; } virtual bool isGroupPath() const { return false; }
virtual bool isFilter() const { return false; } virtual bool isFilter() const { return false; }
virtual ExceptionPathType type() const = 0; 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_; } ExceptionFrom *from() const { return from_; }
ExceptionThruSeq *thrus() const { return thrus_; } ExceptionThruSeq *thrus() const { return thrus_; }
ExceptionTo *to() const { return to_; } ExceptionTo *to() const { return to_; }
@ -127,14 +128,14 @@ public:
virtual bool useEndClk() const { return false; } virtual bool useEndClk() const { return false; }
virtual int pathMultiplier() const { return 0; } virtual int pathMultiplier() const { return 0; }
virtual float delay() const { return 0.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 isDefault() const { return false; }
virtual bool ignoreClkLatency() const { return false; } virtual bool ignoreClkLatency() const { return false; }
virtual bool breakPath() const { return false; } virtual bool breakPath() const { return false; }
protected: protected:
virtual const char *typeString() const = 0; virtual const char *typeString() const = 0;
const char *fromThruToString(const Network *network) const; std::string fromThruToString(const Network *network) const;
void makeStates(); void makeStates();
ExceptionFrom *from_; ExceptionFrom *from_;
@ -209,7 +210,7 @@ public:
bool own_pts) override; bool own_pts) override;
bool isPathDelay() const override { return true; } bool isPathDelay() const override { return true; }
ExceptionPathType type() const override { return ExceptionPathType::path_delay; } 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; const char *typeString() const override;
bool mergeable(ExceptionPath *exception) const override; bool mergeable(ExceptionPath *exception) const override;
bool overrides(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override;
@ -245,7 +246,7 @@ public:
ExceptionPathType type() const override { return ExceptionPathType::multi_cycle; } ExceptionPathType type() const override { return ExceptionPathType::multi_cycle; }
bool matches(const MinMax *min_max, bool matches(const MinMax *min_max,
bool exactly) const override; 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; const char *typeString() const override;
bool mergeable(ExceptionPath *exception) const override; bool mergeable(ExceptionPath *exception) const override;
bool overrides(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override;
@ -292,7 +293,7 @@ public:
class GroupPath : public ExceptionPath class GroupPath : public ExceptionPath
{ {
public: public:
GroupPath(const char *name, GroupPath(const std::string &name,
bool is_default, bool is_default,
ExceptionFrom *from, ExceptionFrom *from,
ExceptionThruSeq *thrus, ExceptionThruSeq *thrus,
@ -311,11 +312,11 @@ public:
bool overrides(ExceptionPath *exception) const override; bool overrides(ExceptionPath *exception) const override;
int typePriority() const override; int typePriority() const override;
bool tighterThan(ExceptionPath *exception) 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_; } bool isDefault() const override { return is_default_; }
protected: protected:
const char *name_; std::string name_;
bool is_default_; bool is_default_;
}; };
@ -343,7 +344,7 @@ public:
// All pins and instance/net pins. // All pins and instance/net pins.
virtual PinSet allPins(const Network *network) = 0; virtual PinSet allPins(const Network *network) = 0;
virtual int typePriority() const = 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 size_t objectCount() const = 0;
virtual void addPin(const Pin *pin, virtual void addPin(const Pin *pin,
const Network *network) = 0; const Network *network) = 0;
@ -367,8 +368,8 @@ protected:
// exception merging. // exception merging.
size_t hash_; size_t hash_;
// Maximum number of objects for asString() to show. // Maximum number of objects for to_string() to show.
static const int as_string_max_objects_; static const int to_string_max_objects_;
static const size_t hash_clk = 3; static const size_t hash_clk = 3;
static const size_t hash_pin = 5; static const size_t hash_pin = 5;
static const size_t hash_net = 7; static const size_t hash_net = 7;
@ -402,7 +403,7 @@ public:
const Network *network) const override; const Network *network) const override;
void mergeInto(ExceptionPt *pt, void mergeInto(ExceptionPt *pt,
const Network *network) override; 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; size_t objectCount() const override;
void deleteClock(Clock *clk); void deleteClock(Clock *clk);
void addPin(const Pin *pin, void addPin(const Pin *pin,
@ -467,7 +468,7 @@ public:
const Network *network); const Network *network);
ExceptionTo *clone(const Network *network); ExceptionTo *clone(const Network *network);
bool isTo() const override { return true; } 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_; } const RiseFallBoth *endTransition() { return end_rf_; }
bool intersectsPts(ExceptionTo *to, bool intersectsPts(ExceptionTo *to,
const Network *network) const; const Network *network) const;
@ -512,7 +513,7 @@ public:
const Network *network); const Network *network);
~ExceptionThru(); ~ExceptionThru();
ExceptionThru *clone(const Network *network); 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; } bool isThru() const override { return true; }
PinSet *pins() override { return pins_; } PinSet *pins() override { return pins_; }
EdgePinsSet *edges() override { return edges_; } 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 class Graph : public StaState
{ {
public: 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, Graph(StaState *sta,
int slew_rf_count,
DcalcAPIndex ap_count); DcalcAPIndex ap_count);
void makeGraph(); void makeGraph();
~Graph(); ~Graph();
@ -97,9 +91,11 @@ public:
// Reported slew are the same as those in the liberty tables. // Reported slew are the same as those in the liberty tables.
// reported_slews = measured_slews / slew_derate_from_library // reported_slews = measured_slews / slew_derate_from_library
// Measured slews are between slew_lower_threshold and slew_upper_threshold. // Measured slews are between slew_lower_threshold and slew_upper_threshold.
const Slew &slew(const Vertex *vertex, Slew slew(const Vertex *vertex,
const RiseFall *rf, const RiseFall *rf,
DcalcAPIndex ap_index); DcalcAPIndex ap_index);
Slew slew(const Vertex *vertex,
size_t index);
void setSlew(Vertex *vertex, void setSlew(Vertex *vertex,
const RiseFall *rf, const RiseFall *rf,
DcalcAPIndex ap_index, DcalcAPIndex ap_index,
@ -134,9 +130,9 @@ public:
void setArcDelay(Edge *edge, void setArcDelay(Edge *edge,
const TimingArc *arc, const TimingArc *arc,
DcalcAPIndex ap_index, DcalcAPIndex ap_index,
ArcDelay delay); const ArcDelay &delay);
// Alias for arcDelays using library wire arcs. // Alias for arcDelays using library wire arcs.
const ArcDelay &wireArcDelay(const Edge *edge, ArcDelay wireArcDelay(const Edge *edge,
const RiseFall *rf, const RiseFall *rf,
DcalcAPIndex ap_index); DcalcAPIndex ap_index);
void setWireArcDelay(Edge *edge, void setWireArcDelay(Edge *edge,
@ -222,7 +218,6 @@ protected:
// driver/source (top level input, instance pin output) vertex // driver/source (top level input, instance pin output) vertex
// in pin_bidirect_drvr_vertex_map // in pin_bidirect_drvr_vertex_map
PinVertexMap pin_bidirect_drvr_vertex_map_; PinVertexMap pin_bidirect_drvr_vertex_map_;
int slew_rf_count_;
DcalcAPIndex ap_count_; DcalcAPIndex ap_count_;
// Sdf period check annotations. // Sdf period check annotations.
PeriodCheckAnnotations *period_check_annotations_; PeriodCheckAnnotations *period_check_annotations_;
@ -258,8 +253,6 @@ public:
[[nodiscard]] bool isRoot() const{ return level_ == 0; } [[nodiscard]] bool isRoot() const{ return level_ == 0; }
[[nodiscard]] bool hasFanin() const; [[nodiscard]] bool hasFanin() const;
[[nodiscard]] bool hasFanout() const; [[nodiscard]] bool hasFanout() const;
Slew *slews() { return slews_; }
const Slew *slews() const { return slews_; }
Path *paths() const { return paths_; } Path *paths() const { return paths_; }
Path *makePaths(uint32_t count); Path *makePaths(uint32_t count);
void setPaths(Path *paths); void setPaths(Path *paths);
@ -298,14 +291,18 @@ protected:
bool is_bidirect_drvr, bool is_bidirect_drvr,
bool is_reg_clk); bool is_reg_clk);
void clear(); 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_; Pin *pin_;
EdgeId in_edges_; // Edges to this vertex. EdgeId in_edges_; // Edges to this vertex.
EdgeId out_edges_; // Edges from this vertex. EdgeId out_edges_; // Edges from this vertex.
// Delay calc // Delay calc
Slew *slews_; float *slews_;
// Search // Search
Path *paths_; Path *paths_;
@ -356,8 +353,9 @@ public:
TimingSense sense() const; TimingSense sense() const;
TimingArcSet *timingArcSet() const { return arc_set_; } TimingArcSet *timingArcSet() const { return arc_set_; }
void setTimingArcSet(TimingArcSet *set); void setTimingArcSet(TimingArcSet *set);
ArcDelay *arcDelays() const { return arc_delays_; } float *arcDelays() { return arc_delays_; }
void setArcDelays(ArcDelay *arc_delays); const float *arcDelays() const { return arc_delays_; }
void setArcDelays(float *delays);
bool delay_Annotation_Is_Incremental() const {return delay_annotation_is_incremental_;}; bool delay_Annotation_Is_Incremental() const {return delay_annotation_is_incremental_;};
void setDelayAnnotationIsIncremental(bool is_incr); void setDelayAnnotationIsIncremental(bool is_incr);
// Edge is disabled to break combinational loops. // Edge is disabled to break combinational loops.
@ -398,7 +396,7 @@ protected:
EdgeId vertex_in_link_; // Vertex in edges list. EdgeId vertex_in_link_; // Vertex in edges list.
EdgeId vertex_out_next_; // Vertex out edges doubly linked list. EdgeId vertex_out_next_; // Vertex out edges doubly linked list.
EdgeId vertex_out_prev_; EdgeId vertex_out_prev_;
ArcDelay *arc_delays_; float *arc_delays_;
union { union {
uintptr_t bits_; uintptr_t bits_;
std::vector<bool> *seq_; std::vector<bool> *seq_;

View File

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

View File

@ -33,44 +33,11 @@
namespace sta { 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 class InternalPowerModel
{ {
public: public:
InternalPowerModel(TableModel *model); InternalPowerModel();
~InternalPowerModel(); InternalPowerModel(std::shared_ptr<TableModel> model);
float power(const LibertyCell *cell, float power(const LibertyCell *cell,
const Pvt *pvt, const Pvt *pvt,
float in_slew, float in_slew,
@ -80,7 +47,7 @@ public:
float in_slew, float in_slew,
float load_cap, float load_cap,
int digits) const; int digits) const;
const TableModel *model() const { return model_; } const TableModel *model() const { return model_.get(); }
protected: protected:
void findAxisValues(float in_slew, void findAxisValues(float in_slew,
@ -95,7 +62,36 @@ protected:
bool checkAxes(const TableModel *model); bool checkAxes(const TableModel *model);
bool checkAxis(const TableAxis *axis); 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 } // namespace

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,15 +27,23 @@
#include <stdio.h> #include <stdio.h>
#include <cstdarg> #include <cstdarg>
#include <string> #include <string>
#include <string_view>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include "Machine.hh" // __attribute__ #include "Machine.hh" // __attribute__
#include "Format.hh"
struct Tcl_Interp; struct Tcl_Interp;
namespace sta { 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. // Output streams used for printing.
// This is a wrapper for all printing. It supports logging output to // This is a wrapper for all printing. It supports logging output to
// a file and redirection of command output to a file. // a file and redirection of command output to a file.
@ -45,74 +53,137 @@ public:
Report(); Report();
virtual ~Report(); virtual ~Report();
// Print line with return. virtual void reportLine(const std::string &line);
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 reportBlankLine(); 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. // Report warning.
virtual void warn(int id, template <typename... Args>
const char *fmt, ...) void warn(int id,
__attribute__((format (printf, 3, 4))); std::string_view fmt,
virtual void vwarn(int id, Args &&...args)
const char *fmt, {
va_list args); if (!isSuppressed(id))
// Report warning in a file. warnMsg(id, sta::vformat(fmt, sta::make_format_args(args...)));
virtual void fileWarn(int id, }
const char *filename, virtual void warnMsg(int id,
int line, const std::string &formatted_msg) {
const char *fmt, ...) reportLine(sta::format("Warning {}: {}", id, formatted_msg));
__attribute__((format (printf, 5, 6))); }
virtual void vfileWarn(int id,
const char *filename,
int line,
const char *fmt,
va_list args);
virtual void error(int id, // Report warning in a file.
const char *fmt, ...) template <typename... Args>
__attribute__((format (printf, 3, 4))); void fileWarn(int id,
virtual void verror(int id, std::string_view filename,
const char *fmt, int line,
va_list args); 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));
}
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. // Report error in a file.
virtual void fileError(int id, template <typename... Args>
const char *filename, void fileError(int id,
std::string_view filename,
int line, int line,
const char *fmt, ...) std::string_view fmt,
__attribute__((format (printf, 5, 6))); Args &&...args)
virtual void vfileError(int id, {
const char *filename, fileErrorMsg(id, filename, line,
sta::vformat(fmt, sta::make_format_args(args...)));
}
virtual void fileErrorMsg(int id,
std::string_view filename,
int line, int line,
const char *fmt, const std::string &formatted_msg)
va_list args); {
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. // Report error condition that should not be possible or that prevents execution.
// The default handler prints msg to stderr and exits. // The default handler prints msg to stderr and exits.
virtual void critical(int id, template <typename... Args>
const char *fmt, void critical(int id,
...) std::string_view fmt,
__attribute__((format (printf, 3, 4))); Args &&...args)
virtual void fileCritical(int id, {
const char *filename, 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, int line,
const char *fmt, std::string_view fmt,
...) Args &&...args)
__attribute__((format (printf, 5, 6))); {
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. // Log output to filename until logEnd is called.
virtual void logBegin(const char *filename); virtual void logBegin(std::string filename);
virtual void logEnd(); virtual void logEnd();
// Redirect output to filename until redirectFileEnd is called. // 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. // Redirect append output to filename until redirectFileEnd is called.
virtual void redirectFileAppendBegin(const char *filename); virtual void redirectFileAppendBegin(std::string filename);
virtual void redirectFileEnd(); virtual void redirectFileEnd();
// Redirect output to a string until redirectStringEnd is called. // Redirect output to a string until redirectStringEnd is called.
virtual void redirectStringBegin(); virtual void redirectStringBegin();
@ -139,9 +210,7 @@ protected:
// Return the number of characters written. // Return the number of characters written.
virtual size_t printConsole(const char *buffer, virtual size_t printConsole(const char *buffer,
size_t length); size_t length);
void printToBuffer(const char *fmt, void printToBuffer(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
...)
__attribute__((format (printf, 2, 3)));
void printToBuffer(const char *fmt, void printToBuffer(const char *fmt,
va_list args); va_list args);
@ -169,4 +238,4 @@ protected:
friend class Debug; friend class Debug;
}; };
} // namespace } // namespace sta

View File

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

View File

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

View File

@ -274,7 +274,7 @@ protected:
const PatternMatch *pattern, const PatternMatch *pattern,
InstanceSeq &matches) const; 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. // Encapsulate a network to map names to/from the sdc namespace.

View File

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

View File

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

View File

@ -143,14 +143,6 @@ public:
char * char *
stringCopy(const char *str); stringCopy(const char *str);
inline void
stringAppend(char *&str1,
const char *str2)
{
strcpy(str1, str2);
str1 += strlen(str2);
}
void void
stringDeleteCheck(const char *str); stringDeleteCheck(const char *str);
@ -164,32 +156,6 @@ stringDelete(const char *str)
bool bool
isDigits(const char *str); 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 * char *
makeTmpString(size_t length); makeTmpString(size_t length);
char * char *

View File

@ -33,12 +33,14 @@
#include "Transition.hh" #include "Transition.hh"
#include "LibertyClass.hh" #include "LibertyClass.hh"
#include "TimingModel.hh" #include "TimingModel.hh"
#include "Variables.hh"
namespace sta { namespace sta {
class Unit; class Unit;
class Units; class Units;
class Report; class Report;
class TableModels;
class Table; class Table;
class TableModel; class TableModel;
class TableAxis; class TableAxis;
@ -63,43 +65,41 @@ class GateTableModel : public GateTimingModel
{ {
public: public:
GateTableModel(LibertyCell *cell, GateTableModel(LibertyCell *cell,
TableModel *delay_model, TableModels *delay_models,
TableModelsEarlyLate delay_sigma_models, TableModels *slew_models,
TableModel *slew_model,
TableModelsEarlyLate slew_sigma_models,
ReceiverModelPtr receiver_model, ReceiverModelPtr receiver_model,
OutputWaveforms *output_waveforms); OutputWaveforms *output_waveforms);
GateTableModel(LibertyCell *cell, GateTableModel(LibertyCell *cell,
TableModel *delay_model, TableModels *delay_models,
TableModel *slew_model); TableModels *slew_models);
~GateTableModel() override; ~GateTableModel() override;
void gateDelay(const Pvt *pvt, void gateDelay(const Pvt *pvt,
float in_slew, float in_slew,
float load_cap, float load_cap,
bool pocv_enabled, // Return values.
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. // Return values.
ArcDelay &gate_delay, ArcDelay &gate_delay,
Slew &drvr_slew) const override; 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));
std::string reportGateDelay(const Pvt *pvt, std::string reportGateDelay(const Pvt *pvt,
float in_slew, float in_slew,
float load_cap, float load_cap,
bool pocv_enabled, const MinMax *min_max,
PocvMode pocv_mode,
int digits) const override; int digits) const override;
float driveResistance(const Pvt *pvt) const override; float driveResistance(const Pvt *pvt) const override;
const TableModel *delayModel() const { return delay_model_.get(); } const TableModels *delayModels() const { return delay_models_.get(); }
const TableModel *slewModel() const { return slew_model_.get(); } const TableModel *delayModel() const;
const TableModel *delaySigmaModel(const EarlyLate *el) const; const TableModels *slewModels() const { return slew_models_.get(); }
const TableModel *slewSigmaModel(const EarlyLate *el) const; const TableModel *slewModel() const;
const ReceiverModel *receiverModel() const { return receiver_model_.get(); } const ReceiverModel *receiverModel() const { return receiver_model_.get(); }
OutputWaveforms *outputWaveforms() const { return output_waveforms_.get(); } OutputWaveforms *outputWaveforms() const { return output_waveforms_.get(); }
// Check the axes before making the model. // Check the axes before making the model.
@ -138,10 +138,8 @@ protected:
float &axis_value3) const; float &axis_value3) const;
static bool checkAxis(const TableAxis *axis); static bool checkAxis(const TableAxis *axis);
std::unique_ptr<TableModel> delay_model_; std::unique_ptr<TableModels> delay_models_;
TableModelsEarlyLate delay_sigma_models_; std::unique_ptr<TableModels> slew_models_;
std::unique_ptr<TableModel> slew_model_;
TableModelsEarlyLate slew_sigma_models_;
ReceiverModelPtr receiver_model_; ReceiverModelPtr receiver_model_;
std::unique_ptr<OutputWaveforms> output_waveforms_; std::unique_ptr<OutputWaveforms> output_waveforms_;
}; };
@ -150,25 +148,24 @@ class CheckTableModel : public CheckTimingModel
{ {
public: public:
CheckTableModel(LibertyCell *cell, CheckTableModel(LibertyCell *cell,
TableModel *model, TableModels *check_models);
TableModelsEarlyLate sigma_models);
CheckTableModel(LibertyCell *cell,
TableModel *model);
~CheckTableModel() override; ~CheckTableModel() override;
ArcDelay checkDelay(const Pvt *pvt, ArcDelay checkDelay(const Pvt *pvt,
float from_slew, float from_slew,
float to_slew, float to_slew,
float related_out_cap, float related_out_cap,
bool pocv_enabled) const override; const MinMax *min_max,
PocvMode pocv_mode) const override;
std::string reportCheckDelay(const Pvt *pvt, std::string reportCheckDelay(const Pvt *pvt,
float from_slew, float from_slew,
const char *from_slew_annotation, const char *from_slew_annotation,
float to_slew, float to_slew,
float related_out_cap, float related_out_cap,
bool pocv_enabled, const MinMax *min_max,
PocvMode pocv_mode,
int digits) const override; int digits) const override;
const TableModel *model() const { return model_.get(); } const TableModels *checkModels() const { return check_models_.get(); }
const TableModel *sigmaModel(const EarlyLate *el) const; const TableModel *checkModel() const;
// Check the axes before making the model. // Check the axes before making the model.
// Return true if the model axes are supported. // Return true if the model axes are supported.
@ -202,8 +199,7 @@ protected:
int digits) const; int digits) const;
static bool checkAxis(const TableAxis *axis); static bool checkAxis(const TableAxis *axis);
std::unique_ptr<TableModel> model_; std::unique_ptr<TableModels> check_models_;
TableModelsEarlyLate sigma_models_;
}; };
class TableAxis class TableAxis
@ -311,6 +307,8 @@ public:
private: private:
void clear(); 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, std::string reportValueOrder0(const char *result_name,
const char *comment1, const char *comment1,
const Unit *table_unit, const Unit *table_unit,
@ -408,6 +406,34 @@ protected:
bool is_scaled_:1; 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 class ReceiverModel

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -80,8 +80,7 @@ LibExprReader::makeFuncExprPort(const char *port_name)
if (port) if (port)
expr = FuncExpr::makePort(port); expr = FuncExpr::makePort(port);
else else
report_->warn(1130, "%s references unknown port %s.", report_->warn(1130, "{} references unknown port {}.", error_msg_, port_name);
error_msg_, port_name);
stringDelete(port_name); stringDelete(port_name);
return expr; return expr;
} }
@ -134,7 +133,7 @@ LibExprReader::setResult(FuncExpr *result)
void void
LibExprReader::parseError(const char *msg) 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 "Liberty.hh"
#include "ContainerHelpers.hh" #include "ContainerHelpers.hh"
#include "Format.hh"
#include "Mutex.hh" #include "Mutex.hh"
#include "EnumNameMap.hh" #include "EnumNameMap.hh"
#include "Report.hh" #include "Report.hh"
@ -775,7 +776,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1,
port1->setScenePort(port2, ap_index); port1->setScenePort(port2, ap_index);
} }
else 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->library()->name(),
cell1->name(), cell1->name(),
port_name, port_name,
@ -801,7 +802,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1,
} }
} }
else 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->library()->name(),
cell1->name(), cell1->name(),
arc_set1->from() ? arc_set1->from()->name() : "", arc_set1->from() ? arc_set1->from()->name() : "",
@ -820,7 +821,7 @@ LibertyLibrary::checkScenes(LibertyCell *cell,
for (const Scene *scene : scenes) { for (const Scene *scene : scenes) {
for (auto min_max : MinMax::range()) { for (auto min_max : MinMax::range()) {
if (!cell->checkSceneCell(scene, min_max)) 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->libertyLibrary()->name(),
cell->name(), cell->name(),
scene->name().c_str(), scene->name().c_str(),
@ -1262,7 +1263,7 @@ LibertyCell::makeInternalPower(LibertyPort *port,
LibertyPort *related_port, LibertyPort *related_port,
LibertyPort *related_pg_pin, LibertyPort *related_pg_pin,
const std::shared_ptr<FuncExpr> &when, const std::shared_ptr<FuncExpr> &when,
InternalPowerModels &models) const InternalPowerModels &models)
{ {
internal_powers_.emplace_back(port, related_port, related_pg_pin, when, models); internal_powers_.emplace_back(port, related_port, related_pg_pin, when, models);
port_internal_powers_[port].push_back(internal_powers_.size() - 1); 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); TimingSense en_sense = en_func->portTimingSense(en);
if (en_sense == TimingSense::positive_unate if (en_sense == TimingSense::positive_unate
&& en_rf != RiseFall::rise()) && 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(), library_->name(),
name(), name(),
en->name(), en->name(),
@ -1711,7 +1712,7 @@ LibertyCell::makeLatchEnables(Report *report,
en_rf == RiseFall::rise()?"rising":"falling"); en_rf == RiseFall::rise()?"rising":"falling");
else if (en_sense == TimingSense::negative_unate else if (en_sense == TimingSense::negative_unate
&& en_rf != RiseFall::fall()) && 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(), library_->name(),
name(), name(),
en->name(), en->name(),
@ -1721,7 +1722,7 @@ LibertyCell::makeLatchEnables(Report *report,
} }
} }
else 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(), library_->name(),
name(), name(),
d->name(), d->name(),
@ -1767,7 +1768,7 @@ LibertyCell::findLatchSetup(const LibertyPort *d,
for (TimingArc *arc : arc_set->arcs()) { for (TimingArc *arc : arc_set->arcs()) {
const RiseFall *from_rf = arc->fromEdge()->asRiseFall(); const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
if (from_rf == en_rf) { 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(), library_->name(),
name(), name(),
en->name(), en->name(),
@ -1824,7 +1825,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d,
latch_check_map_[setup_check] = idx; latch_check_map_[setup_check] = idx;
d->setIsLatchData(true); d->setIsLatchData(true);
debugPrint(debug, "liberty_latch", 1, debugPrint(debug, "liberty_latch", 1,
"latch %s -> %s | %s %s -> %s | %s %s -> %s setup", "latch {} -> {} | {} {} -> {} | {} {} -> {} setup",
d->name(), d->name(),
q->name(), q->name(),
en->name(), en->name(),
@ -2904,7 +2905,7 @@ ModeDef::defineValue(const char *value,
const char *sdf_cond) const char *sdf_cond)
{ {
std::string key = value; 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)); auto [it, inserted] = values_.try_emplace(key, key, cond, std::move(sdf));
return &it->second; return &it->second;
} }
@ -3041,7 +3042,7 @@ OperatingConditions::OperatingConditions(const char *name) :
Pvt(0.0, 0.0, 0.0), Pvt(0.0, 0.0, 0.0),
name_(name), name_(name),
// Default wireload tree. // Default wireload tree.
wire_load_tree_(WireloadTree::balanced) wire_load_tree_(WireloadTree::unknown)
{ {
} }
@ -3202,27 +3203,27 @@ ScaleFactors::report(Report *report)
std::string line = " "; std::string line = " ";
for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) { for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
ScaleFactorPvt pvt = (ScaleFactorPvt) 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++) { for (int type_index = 0; type_index < scale_factor_type_count; type_index++) {
ScaleFactorType type = (ScaleFactorType) 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++) { for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
if (scaleFactorTypeRiseFallSuffix(type) if (scaleFactorTypeRiseFallSuffix(type)
|| scaleFactorTypeRiseFallPrefix(type) || scaleFactorTypeRiseFallPrefix(type)
|| scaleFactorTypeLowHighSuffix(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::riseIndex()],
scales_[type_index][pvt_index][RiseFall::fallIndex()]); scales_[type_index][pvt_index][RiseFall::fallIndex()]);
} }
else { else {
stringAppend(line, " %.3f", line += sta::format(" {:.3f}",
scales_[type_index][pvt_index][0]); 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 TimingRole *role() { return self->role(); }
const char *sdf_cond() { return self->sdfCond().c_str(); } const char *sdf_cond() { return self->sdfCond().c_str(); }
const char * std::string
full_name() full_name()
{ {
const char *from = self->from()->name(); const char *from = self->from()->name();
const char *to = self->to()->name(); const char *to = self->to()->name();
const char *cell_name = self->libertyCell()->name(); const char *cell_name = self->libertyCell()->name();
return stringPrintTmp("%s %s -> %s", return sta::format("{} {} -> {}", cell_name, from, to);
cell_name,
from,
to);
} }
const std::string const std::string

View File

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

View File

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

View File

@ -130,10 +130,8 @@ LibertyParser::groupBegin(const std::string type,
LibertyAttrValueSeq *params, LibertyAttrValueSeq *params,
int line) int line)
{ {
LibertyGroup *group = LibertyGroup *group = new LibertyGroup(
new LibertyGroup(std::move(type), std::move(type), params ? std::move(*params) : LibertyAttrValueSeq(), line);
params ? std::move(*params) : LibertyAttrValueSeq(),
line);
delete params; delete params;
LibertyGroup *parent_group = group_stack_.empty() ? nullptr : group_stack_.back(); LibertyGroup *parent_group = group_stack_.empty() ? nullptr : group_stack_.back();
group_visitor_->begin(group, parent_group); group_visitor_->begin(group, parent_group);
@ -145,8 +143,7 @@ LibertyParser::groupEnd()
{ {
LibertyGroup *group = this->group(); LibertyGroup *group = this->group();
group_stack_.pop_back(); group_stack_.pop_back();
LibertyGroup *parent = LibertyGroup *parent = group_stack_.empty() ? nullptr : group_stack_.back();
group_stack_.empty() ? nullptr : group_stack_.back();
if (parent) if (parent)
parent->addSubgroup(group); parent->addSubgroup(group);
group_visitor_->end(group, parent); group_visitor_->end(group, parent);
@ -170,8 +167,8 @@ LibertyParser::makeSimpleAttr(const std::string name,
const LibertyAttrValue *value, const LibertyAttrValue *value,
int line) int line)
{ {
LibertySimpleAttr *attr = new LibertySimpleAttr(std::move(name), LibertySimpleAttr *attr =
std::move(*value), line); new LibertySimpleAttr(std::move(name), std::move(*value), line);
delete value; delete value;
LibertyGroup *group = this->group(); LibertyGroup *group = this->group();
group->addAttr(attr); 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 return nullptr; // Define is not a complex attr; already added to group
} }
else { else {
LibertyComplexAttr *attr = new LibertyComplexAttr(std::move(name), LibertyComplexAttr *attr =
std::move(*values), new LibertyComplexAttr(std::move(name), std::move(*values), line);
line);
delete values; delete values;
LibertyGroup *group = this->group(); LibertyGroup *group = this->group();
group->addAttr(attr); group->addAttr(attr);
@ -266,7 +262,7 @@ LibertyScanner::includeBegin()
} }
else { else {
report_->fileWarn(25, filename_.c_str(), yylineno, report_->fileWarn(25, filename_.c_str(), yylineno,
"cannot open include file %s.", filename.c_str()); "cannot open include file {}.", filename);
delete stream; delete stream;
} }
} }
@ -291,7 +287,7 @@ LibertyScanner::fileEnd()
void void
LibertyScanner::error(const char *msg) 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() LibertyGroup::~LibertyGroup() { clear(); }
{
clear();
}
void void
LibertyGroup::clear() LibertyGroup::clear()
@ -327,19 +320,15 @@ LibertyGroup::clear()
bool bool
LibertyGroup::empty() const LibertyGroup::empty() const
{ {
return subgroups_.empty() return subgroups_.empty() && simple_attr_map_.empty() && complex_attr_map_.empty()
&& simple_attr_map_.empty()
&& complex_attr_map_.empty()
&& define_map_.empty(); && define_map_.empty();
} }
bool bool
LibertyGroup::oneGroupOnly() const LibertyGroup::oneGroupOnly() const
{ {
return subgroups_.size() == 1 return subgroups_.size() == 1 && simple_attr_map_.empty()
&& simple_attr_map_.empty() && complex_attr_map_.empty() && define_map_.empty();
&& complex_attr_map_.empty()
&& define_map_.empty();
} }
void void
@ -538,10 +527,7 @@ LibertyComplexAttr::LibertyComplexAttr(std::string name,
{ {
} }
LibertyComplexAttr::~LibertyComplexAttr() LibertyComplexAttr::~LibertyComplexAttr() { deleteContents(values_); }
{
deleteContents(values_);
}
const LibertyAttrValue * const LibertyAttrValue *
LibertyComplexAttr::firstValue() const LibertyComplexAttr::firstValue() const
@ -585,7 +571,7 @@ LibertyAttrValue::floatValue() const
} }
void void
LibertyAttrValue::floatValue(// Return values. LibertyAttrValue::floatValue( // Return values.
float &value, float &value,
bool &valid) const bool &valid) const
{ {
@ -598,8 +584,7 @@ LibertyAttrValue::floatValue(// Return values.
// Some floats are enclosed in quotes. // Some floats are enclosed in quotes.
char *end; char *end;
value = strtof(string_value_.c_str(), &end); value = strtof(string_value_.c_str(), &end);
if ((*end == '\0' if ((*end == '\0' || isspace(*end))
|| isspace(*end))
// strtof support INF as a valid float. // strtof support INF as a valid float.
&& string_value_ != "inf") { && string_value_ != "inf") {
valid = true; 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 <functional>
#include <memory> #include <memory>
#include <array> #include <array>
#include <string_view>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
@ -44,6 +45,7 @@
#include "LibertyParser.hh" #include "LibertyParser.hh"
#include "LibertyReader.hh" #include "LibertyReader.hh"
#include "LibertyBuilder.hh" #include "LibertyBuilder.hh"
#include "Report.hh"
namespace sta { namespace sta {
@ -232,25 +234,21 @@ protected:
const LibertyPortSeq &ports, const LibertyPortSeq &ports,
const LibertyGroup *port_group); const LibertyGroup *port_group);
bool isGateTimingType(TimingType timing_type); bool isGateTimingType(TimingType timing_type);
TableModel *readGateTableModel(const LibertyGroup *timing_group, TableModel *readTableModel(const LibertyGroup *timing_group,
const char *table_group_name, const std::string &table_group_name,
const RiseFall *rf, const RiseFall *rf,
TableTemplateType template_type, TableTemplateType template_type,
float scale, float scale,
ScaleFactorType scale_factor_type); ScaleFactorType scale_factor_type,
const std::function<bool(TableModel *model)> check_axes);
TableModelsEarlyLate TableModelsEarlyLate
readEarlyLateTableModels(const LibertyGroup *timing_group, readEarlyLateTableModels(const LibertyGroup *timing_group,
const char *table_group_name, const char *table_group_name,
const RiseFall *rf, const RiseFall *rf,
TableTemplateType template_type, TableTemplateType template_type,
float scale, float scale,
ScaleFactorType scale_factor_type); ScaleFactorType scale_factor_type,
TableModel *readCheckTableModel(const LibertyGroup *timing_group, const std::function<bool(TableModel *model)> check_axes);
const char *table_group_name,
const RiseFall *rf,
TableTemplateType template_type,
float scale,
ScaleFactorType scale_factor_type);
ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group, ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group,
const RiseFall *rf); const RiseFall *rf);
void readReceiverCapacitance(const LibertyGroup *timing_group, void readReceiverCapacitance(const LibertyGroup *timing_group,
@ -268,7 +266,9 @@ protected:
const RiseFall *rf, const RiseFall *rf,
TableTemplateType template_type, TableTemplateType template_type,
float scale, 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, TablePtr readTableModel(const LibertyGroup *table_group,
const TableTemplate *tbl_template, const TableTemplate *tbl_template,
float scale); float scale);
@ -281,6 +281,14 @@ protected:
void makeTableModels(LibertyCell *cell, void makeTableModels(LibertyCell *cell,
const LibertyGroup *timing_group, const LibertyGroup *timing_group,
TimingArcAttrsPtr timing_attrs); 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, TableAxisPtr makeTableAxis(const LibertyGroup *table_group,
const char *index_attr_name, const char *index_attr_name,
@ -445,38 +453,65 @@ protected:
const char *attr_name, const char *attr_name,
const LibertyCell *cell, const LibertyCell *cell,
int line); int line);
void libWarn(int id, template <typename... Args>
void warn(int id,
const LibertyGroup *group, const LibertyGroup *group,
const char *fmt, std::string_view fmt,
...) const Args &&...args) const
__attribute__((format (printf, 4, 5))); {
void libWarn(int id, report_->fileWarn(id, filename_, group->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
const LibertySimpleAttr *attr, const LibertySimpleAttr *attr,
const char *fmt, std::string_view fmt,
...) const Args &&...args) const
__attribute__((format (printf, 4, 5))); {
void libWarn(int id, report_->fileWarn(id, filename_, attr->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
const LibertyComplexAttr *attr, const LibertyComplexAttr *attr,
const char *fmt, std::string_view fmt,
...) const Args &&...args) const
__attribute__((format (printf, 4, 5))); {
void libWarn(int id, report_->fileWarn(id, filename_, attr->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
int line, int line,
const char *fmt, std::string_view fmt,
...) const Args &&...args) const
__attribute__((format (printf, 4, 5))); {
void libError(int id, report_->fileWarn(id, filename_, line, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertyGroup *group, const LibertyGroup *group,
const char *fmt, ...) const std::string_view fmt,
__attribute__((format (printf, 4, 5))); Args &&...args) const
void libError(int id, {
report_->fileError(id, filename_, group->line(), fmt,
std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertySimpleAttr *attr, const LibertySimpleAttr *attr,
const char *fmt, ...) const std::string_view fmt,
__attribute__((format (printf, 4, 5))); Args &&...args) const
void libError(int id, {
report_->fileError(id, filename_, attr->line(), fmt,
std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertyComplexAttr *attr, const LibertyComplexAttr *attr,
const char *fmt, ...) const std::string_view fmt,
__attribute__((format (printf, 4, 5))); Args &&...args) const
{
report_->fileError(id, filename_, attr->line(), fmt,
std::forward<Args>(args)...);
}
const char *filename_; const char *filename_;
bool infer_latches_; bool infer_latches_;

View File

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

View File

@ -26,7 +26,9 @@
#include <cstdlib> #include <cstdlib>
#include <cmath> #include <cmath>
#include <fstream>
#include "Format.hh"
#include "Units.hh" #include "Units.hh"
#include "FuncExpr.hh" #include "FuncExpr.hh"
#include "PortDirection.hh" #include "PortDirection.hh"
@ -44,7 +46,7 @@ class LibertyWriter
public: public:
LibertyWriter(const LibertyLibrary *lib, LibertyWriter(const LibertyLibrary *lib,
const char *filename, const char *filename,
FILE *stream, std::ofstream &stream,
Report *report); Report *report);
void writeLibrary(); void writeLibrary();
@ -80,7 +82,7 @@ protected:
const LibertyLibrary *library_; const LibertyLibrary *library_;
const char *filename_; const char *filename_;
FILE *stream_; std::ofstream &stream_;
Report *report_; Report *report_;
const Unit *time_unit_; const Unit *time_unit_;
const Unit *cap_unit_; const Unit *cap_unit_;
@ -91,11 +93,10 @@ writeLiberty(LibertyLibrary *lib,
const char *filename, const char *filename,
StaState *sta) StaState *sta)
{ {
FILE *stream = fopen(filename, "w"); std::ofstream stream(filename);
if (stream) { if (stream.is_open()) {
LibertyWriter writer(lib, filename, stream, sta->report()); LibertyWriter writer(lib, filename, stream, sta->report());
writer.writeLibrary(); writer.writeLibrary();
fclose(stream);
} }
else else
throw FileNotWritable(filename); throw FileNotWritable(filename);
@ -103,7 +104,7 @@ writeLiberty(LibertyLibrary *lib,
LibertyWriter::LibertyWriter(const LibertyLibrary *lib, LibertyWriter::LibertyWriter(const LibertyLibrary *lib,
const char *filename, const char *filename,
FILE *stream, std::ofstream &stream,
Report *report) : Report *report) :
library_(lib), library_(lib),
filename_(filename), filename_(filename),
@ -118,10 +119,10 @@ void
LibertyWriter::writeLibrary() LibertyWriter::writeLibrary()
{ {
writeHeader(); writeHeader();
fprintf(stream_, "\n"); sta::print(stream_, "\n");
writeTableTemplates(); writeTableTemplates();
writeBusDcls(); writeBusDcls();
fprintf(stream_, "\n"); sta::print(stream_, "\n");
writeCells(); writeCells();
writeFooter(); writeFooter();
} }
@ -129,75 +130,75 @@ LibertyWriter::writeLibrary()
void void
LibertyWriter::writeHeader() LibertyWriter::writeHeader()
{ {
fprintf(stream_, "library (%s) {\n", library_->name()); sta::print(stream_, "library ({}) {{\n", library_->name());
fprintf(stream_, " comment : \"\";\n"); sta::print(stream_, " comment : \"\";\n");
fprintf(stream_, " delay_model : table_lookup;\n"); sta::print(stream_, " delay_model : table_lookup;\n");
fprintf(stream_, " simulation : false;\n"); sta::print(stream_, " simulation : false;\n");
const Unit *cap_unit = library_->units()->capacitanceUnit(); const Unit *cap_unit = library_->units()->capacitanceUnit();
fprintf(stream_, " capacitive_load_unit (1,%s);\n", sta::print(stream_, " capacitive_load_unit (1,{});\n",
cap_unit->scaleAbbrevSuffix().c_str()); cap_unit->scaleAbbrevSuffix());
fprintf(stream_, " leakage_power_unit : 1pW;\n"); sta::print(stream_, " leakage_power_unit : 1pW;\n");
const Unit *current_unit = library_->units()->currentUnit(); const Unit *current_unit = library_->units()->currentUnit();
fprintf(stream_, " current_unit : \"1%s\";\n", sta::print(stream_, " current_unit : \"1{}\";\n",
current_unit->scaleAbbrevSuffix().c_str()); current_unit->scaleAbbrevSuffix());
const Unit *res_unit = library_->units()->resistanceUnit(); const Unit *res_unit = library_->units()->resistanceUnit();
fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n", sta::print(stream_, " pulling_resistance_unit : \"1{}\";\n",
res_unit->scaleAbbrevSuffix().c_str()); res_unit->scaleAbbrevSuffix());
const Unit *time_unit = library_->units()->timeUnit(); const Unit *time_unit = library_->units()->timeUnit();
fprintf(stream_, " time_unit : \"1%s\";\n", sta::print(stream_, " time_unit : \"1{}\";\n",
time_unit->scaleAbbrevSuffix().c_str()); time_unit->scaleAbbrevSuffix());
const Unit *volt_unit = library_->units()->voltageUnit(); const Unit *volt_unit = library_->units()->voltageUnit();
fprintf(stream_, " voltage_unit : \"1%s\";\n", sta::print(stream_, " voltage_unit : \"1{}\";\n",
volt_unit->scaleAbbrevSuffix().c_str()); volt_unit->scaleAbbrevSuffix());
fprintf(stream_, " library_features(report_delay_calculation);\n"); sta::print(stream_, " library_features(report_delay_calculation);\n");
fprintf(stream_, "\n"); sta::print(stream_, "\n");
fprintf(stream_, " input_threshold_pct_rise : %.0f;\n", sta::print(stream_, " input_threshold_pct_rise : {:.0f};\n",
library_->inputThreshold(RiseFall::rise()) * 100); library_->inputThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " input_threshold_pct_fall : %.0f;\n", sta::print(stream_, " input_threshold_pct_fall : {:.0f};\n",
library_->inputThreshold(RiseFall::fall()) * 100); library_->inputThreshold(RiseFall::fall()) * 100);
fprintf(stream_, " output_threshold_pct_rise : %.0f;\n", sta::print(stream_, " output_threshold_pct_rise : {:.0f};\n",
library_->inputThreshold(RiseFall::rise()) * 100); library_->inputThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " output_threshold_pct_fall : %.0f;\n", sta::print(stream_, " output_threshold_pct_fall : {:.0f};\n",
library_->inputThreshold(RiseFall::fall()) * 100); library_->inputThreshold(RiseFall::fall()) * 100);
fprintf(stream_, " slew_lower_threshold_pct_rise : %.0f;\n", sta::print(stream_, " slew_lower_threshold_pct_rise : {:.0f};\n",
library_->slewLowerThreshold(RiseFall::rise()) * 100); library_->slewLowerThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " slew_lower_threshold_pct_fall : %.0f;\n", sta::print(stream_, " slew_lower_threshold_pct_fall : {:.0f};\n",
library_->slewLowerThreshold(RiseFall::fall()) * 100); library_->slewLowerThreshold(RiseFall::fall()) * 100);
fprintf(stream_, " slew_upper_threshold_pct_rise : %.0f;\n", sta::print(stream_, " slew_upper_threshold_pct_rise : {:.0f};\n",
library_->slewUpperThreshold(RiseFall::rise()) * 100); library_->slewUpperThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " slew_upper_threshold_pct_fall : %.0f;\n", sta::print(stream_, " slew_upper_threshold_pct_fall : {:.0f};\n",
library_->slewUpperThreshold(RiseFall::rise()) * 100); library_->slewUpperThreshold(RiseFall::rise()) * 100);
fprintf(stream_, " slew_derate_from_library : %.1f;\n", sta::print(stream_, " slew_derate_from_library : {:.1f};\n",
library_->slewDerateFromLibrary()); library_->slewDerateFromLibrary());
fprintf(stream_, "\n"); sta::print(stream_, "\n");
bool exists; bool exists;
float max_fanout; float max_fanout;
library_->defaultFanoutLoad(max_fanout, exists); library_->defaultFanoutLoad(max_fanout, exists);
if (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; float max_slew;
library_->defaultMaxSlew(max_slew, exists); library_->defaultMaxSlew(max_slew, exists);
if (exists) if (exists)
fprintf(stream_, " default_max_transition : %s;\n", sta::print(stream_, " default_max_transition : {};\n",
time_unit_->asString(max_slew, 3)); time_unit_->asString(max_slew, 3));
float max_cap; float max_cap;
library_->defaultMaxCapacitance(max_cap, exists); library_->defaultMaxCapacitance(max_cap, exists);
if (exists) if (exists)
fprintf(stream_, " default_max_capacitance : %s;\n", sta::print(stream_, " default_max_capacitance : {};\n",
cap_unit_->asString(max_cap, 3)); cap_unit_->asString(max_cap, 3));
float fanout_load; float fanout_load;
library_->defaultFanoutLoad(fanout_load, exists); library_->defaultFanoutLoad(fanout_load, exists);
if (exists) if (exists)
fprintf(stream_, " default_fanout_load : %.2f;\n", fanout_load); sta::print(stream_, " default_fanout_load : {:.2f};\n", fanout_load);
fprintf(stream_, "\n"); sta::print(stream_, "\n");
fprintf(stream_, " nom_process : %.1f;\n", sta::print(stream_, " nom_process : {:.1f};\n",
library_->nominalProcess()); library_->nominalProcess());
fprintf(stream_, " nom_temperature : %.1f;\n", sta::print(stream_, " nom_temperature : {:.1f};\n",
library_->nominalTemperature()); library_->nominalTemperature());
fprintf(stream_, " nom_voltage : %.2f;\n", sta::print(stream_, " nom_voltage : {:.2f};\n",
library_->nominalVoltage()); library_->nominalVoltage());
} }
@ -216,14 +217,14 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
const TableAxis *axis3 = tbl_template->axis3(); const TableAxis *axis3 = tbl_template->axis3();
// skip scalar templates // skip scalar templates
if (axis1) { if (axis1) {
fprintf(stream_, " lu_table_template(%s) {\n", tbl_template->name().c_str()); sta::print(stream_, " lu_table_template({}) {{\n", tbl_template->name());
fprintf(stream_, " variable_1 : %s;\n", sta::print(stream_, " variable_1 : {};\n",
tableVariableString(axis1->variable())); tableVariableString(axis1->variable()));
if (axis2) if (axis2)
fprintf(stream_, " variable_2 : %s;\n", sta::print(stream_, " variable_2 : {};\n",
tableVariableString(axis2->variable())); tableVariableString(axis2->variable()));
if (axis3) if (axis3)
fprintf(stream_, " variable_3 : %s;\n", sta::print(stream_, " variable_3 : {};\n",
tableVariableString(axis3->variable())); tableVariableString(axis3->variable()));
if (axis1 && !axis1->values().empty()) if (axis1 && !axis1->values().empty())
writeTableAxis4(axis1, 1); writeTableAxis4(axis1, 1);
@ -231,7 +232,7 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
writeTableAxis4(axis2, 2); writeTableAxis4(axis2, 2);
if (axis3 && !axis3->values().empty()) if (axis3 && !axis3->values().empty())
writeTableAxis4(axis3, 3); writeTableAxis4(axis3, 3);
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
} }
} }
@ -240,16 +241,16 @@ void
LibertyWriter::writeTableAxis4(const TableAxis *axis, LibertyWriter::writeTableAxis4(const TableAxis *axis,
int index) int index)
{ {
fprintf(stream_, " index_%d(\"", index); sta::print(stream_, " index_{}(\"", index);
const Unit *unit = tableVariableUnit(axis->variable(), library_->units()); const Unit *unit = tableVariableUnit(axis->variable(), library_->units());
bool first = true; bool first = true;
for (size_t i = 0; i < axis->size(); i++) { for (size_t i = 0; i < axis->size(); i++) {
if (!first) if (!first)
fprintf(stream_, ", "); sta::print(stream_, ", ");
fprintf(stream_, "%s", unit->asString(axis->axisValue(i), 5)); sta::print(stream_, "{}", unit->asString(axis->axisValue(i), 5));
first = false; first = false;
} }
fprintf(stream_, "\");\n"); sta::print(stream_, "\");\n");
} }
// indent 10 // indent 10
@ -257,7 +258,7 @@ void
LibertyWriter::writeTableAxis10(const TableAxis *axis, LibertyWriter::writeTableAxis10(const TableAxis *axis,
int index) int index)
{ {
fprintf(stream_, " "); sta::print(stream_, " ");
writeTableAxis4(axis, index); writeTableAxis4(axis, index);
} }
@ -266,13 +267,13 @@ LibertyWriter::writeBusDcls()
{ {
BusDclSeq dcls = library_->busDcls(); BusDclSeq dcls = library_->busDcls();
for (BusDcl *dcl : dcls) { for (BusDcl *dcl : dcls) {
fprintf(stream_, " type (\"%s\") {\n", dcl->name().c_str()); sta::print(stream_, " type (\"{}\") {{\n", dcl->name());
fprintf(stream_, " base_type : array;\n"); sta::print(stream_, " base_type : array;\n");
fprintf(stream_, " data_type : bit;\n"); sta::print(stream_, " data_type : bit;\n");
fprintf(stream_, " bit_width : %d;\n", std::abs(dcl->from() - dcl->to() + 1)); sta::print(stream_, " bit_width : {};\n", std::abs(dcl->from() - dcl->to() + 1));
fprintf(stream_, " bit_from : %d;\n", dcl->from()); sta::print(stream_, " bit_from : {};\n", dcl->from());
fprintf(stream_, " bit_to : %d;\n", dcl->to()); sta::print(stream_, " bit_to : {};\n", dcl->to());
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
} }
} }
@ -289,21 +290,20 @@ LibertyWriter::writeCells()
void void
LibertyWriter::writeCell(const LibertyCell *cell) LibertyWriter::writeCell(const LibertyCell *cell)
{ {
fprintf(stream_, " cell (\"%s\") {\n", cell->name()); sta::print(stream_, " cell (\"{}\") {{\n", cell->name());
float area = cell->area(); float area = cell->area();
if (area > 0.0) if (area > 0.0)
fprintf(stream_, " area : %.3f \n", area); sta::print(stream_, " area : {:.3f} \n", area);
if (cell->isMacro()) if (cell->isMacro())
fprintf(stream_, " is_macro_cell : true;\n"); sta::print(stream_, " is_macro_cell : true;\n");
if (cell->interfaceTiming()) if (cell->interfaceTiming())
fprintf(stream_, " interface_timing : true;\n"); sta::print(stream_, " interface_timing : true;\n");
const char *footprint = cell->footprint(); const char *footprint = cell->footprint();
if (footprint) if (footprint)
fprintf(stream_, " cell_footprint : \"%s\";\n", footprint); sta::print(stream_, " cell_footprint : \"{}\";\n", footprint);
const char *user_function_class = cell->userFunctionClass(); const char *user_function_class = cell->userFunctionClass();
if (user_function_class) if (user_function_class)
fprintf(stream_, " user_function_class : \"%s\";\n", sta::print(stream_, " user_function_class : \"{}\";\n", user_function_class);
user_function_class);
LibertyCellPortIterator port_iter(cell); LibertyCellPortIterator port_iter(cell);
while (port_iter.hasNext()) { while (port_iter.hasNext()) {
@ -314,24 +314,23 @@ LibertyWriter::writeCell(const LibertyCell *cell)
else if (port->isBus()) else if (port->isBus())
writeBusPort(port); writeBusPort(port);
else if (port->isBundle()) else if (port->isBundle())
report_->error(1340, "%s/%s bundled ports not supported.", report_->error(1340, "{}/{} bundled ports not supported.", library_->name(),
library_->name(),
cell->name()); cell->name());
else else
writePort(port); writePort(port);
} }
} }
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
fprintf(stream_, "\n"); sta::print(stream_, "\n");
} }
void void
LibertyWriter::writeBusPort(const LibertyPort *port) LibertyWriter::writeBusPort(const LibertyPort *port)
{ {
fprintf(stream_, " bus(\"%s\") {\n", port->name()); sta::print(stream_, " bus(\"{}\") {{\n", port->name());
if (port->busDcl()) 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); writePortAttrs(port);
LibertyPortMemberIterator member_iter(port); LibertyPortMemberIterator member_iter(port);
@ -339,56 +338,53 @@ LibertyWriter::writeBusPort(const LibertyPort *port)
LibertyPort *member = member_iter.next(); LibertyPort *member = member_iter.next();
writePort(member); writePort(member);
} }
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
} }
void void
LibertyWriter::writePort(const LibertyPort *port) LibertyWriter::writePort(const LibertyPort *port)
{ {
fprintf(stream_, " pin(\"%s\") {\n", port->name()); sta::print(stream_, " pin(\"{}\") {{\n", port->name());
writePortAttrs(port); writePortAttrs(port);
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
} }
void void
LibertyWriter::writePortAttrs(const LibertyPort *port) 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(); auto func = port->function();
if (func if (func
// cannot ref internal ports until sequentials are written // cannot ref internal ports until sequentials are written
&& !(func->port() && !(func->port() && func->port()->direction()->isInternal()))
&& func->port()->direction()->isInternal())) sta::print(stream_, " function : \"{}\";\n", func->to_string());
fprintf(stream_, " function : \"%s\";\n", func->to_string().c_str());
auto tristate_enable = port->tristateEnable(); auto tristate_enable = port->tristateEnable();
if (tristate_enable) { if (tristate_enable) {
if (tristate_enable->op() == FuncExpr::Op::not_) { if (tristate_enable->op() == FuncExpr::Op::not_) {
FuncExpr *three_state = tristate_enable->left(); FuncExpr *three_state = tristate_enable->left();
fprintf(stream_, " three_state : \"%s\";\n", sta::print(stream_, " three_state : \"{}\";\n",
three_state->to_string().c_str()); three_state->to_string());
} }
else { else {
FuncExpr *three_state = tristate_enable->copy()->invert(); FuncExpr *three_state = tristate_enable->copy()->invert();
fprintf(stream_, " three_state : \"%s\";\n", sta::print(stream_, " three_state : \"{}\";\n",
three_state->to_string().c_str()); three_state->to_string());
delete three_state; delete three_state;
} }
} }
if (port->isClock()) if (port->isClock())
fprintf(stream_, " clock : true;\n"); sta::print(stream_, " clock : true;\n");
fprintf(stream_, " capacitance : %s;\n", sta::print(stream_, " capacitance : {};\n",
cap_unit_->asString(port->capacitance(), 4)); cap_unit_->asString(port->capacitance(), 4));
float limit; float limit;
bool exists; bool exists;
port->slewLimit(MinMax::max(), limit, exists); port->slewLimit(MinMax::max(), limit, exists);
if (exists) if (exists)
fprintf(stream_, " max_transition : %s;\n", sta::print(stream_, " max_transition : {};\n", time_unit_->asString(limit, 3));
time_unit_->asString(limit, 3));
port->capacitanceLimit(MinMax::max(), limit, exists); port->capacitanceLimit(MinMax::max(), limit, exists);
if (exists) if (exists)
fprintf(stream_, " max_capacitance : %s;\n", sta::print(stream_, " max_capacitance : {};\n", cap_unit_->asString(limit, 3));
cap_unit_->asString(limit, 3));
for (TimingArcSet *arc_set : port->libertyCell()->timingArcSetsTo(port)) { for (TimingArcSet *arc_set : port->libertyCell()->timingArcSetsTo(port)) {
if (!isAutoWidthArc(port, arc_set)) if (!isAutoWidthArc(port, arc_set))
@ -399,10 +395,10 @@ LibertyWriter::writePortAttrs(const LibertyPort *port)
void void
LibertyWriter::writePwrGndPort(const LibertyPort *port) LibertyWriter::writePwrGndPort(const LibertyPort *port)
{ {
fprintf(stream_, " pg_pin(\"%s\") {\n", port->name()); sta::print(stream_, " pg_pin(\"{}\") {{\n", port->name());
fprintf(stream_, " pg_type : \"%s\";\n", pwrGndTypeName(port->pwrGndType())); sta::print(stream_, " pg_type : \"{}\";\n", pwrGndTypeName(port->pwrGndType()));
fprintf(stream_, " voltage_name : \"%s\";\n", port->voltageName()); sta::print(stream_, " voltage_name : \"{}\";\n", port->voltageName());
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
} }
// Check if arc is added for port min_pulse_width_high/low attribute. // Check if arc is added for port min_pulse_width_high/low attribute.
@ -423,30 +419,27 @@ LibertyWriter::isAutoWidthArc(const LibertyPort *port,
void void
LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set) LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
{ {
fprintf(stream_, " timing() {\n"); sta::print(stream_, " timing() {{\n");
if (arc_set->from()) 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(); TimingSense sense = arc_set->sense();
if (sense != TimingSense::unknown if (sense != TimingSense::unknown && sense != TimingSense::non_unate)
&& sense != TimingSense::non_unate) sta::print(stream_, " timing_sense : {};\n", to_string(sense));
fprintf(stream_, " timing_sense : %s;\n",
to_string(sense));
const char *timing_type = timingTypeString(arc_set); const char *timing_type = timingTypeString(arc_set);
if (timing_type) 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()) { for (const RiseFall *rf : RiseFall::range()) {
TimingArc *arc = arc_set->arcTo(rf); TimingArc *arc = arc_set->arcTo(rf);
if (arc) { if (arc) {
// Min pulse width arcs are wrt to the leading edge of the pulse. // Min pulse width arcs are wrt to the leading edge of the pulse.
const RiseFall *model_rf = (arc_set->role() == TimingRole::width()) const RiseFall *model_rf =
? rf->opposite() (arc_set->role() == TimingRole::width()) ? rf->opposite() : rf;
: rf;
writeTimingModels(arc, model_rf); writeTimingModels(arc, model_rf);
} }
} }
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
} }
void void
@ -454,35 +447,35 @@ LibertyWriter::writeTimingModels(const TimingArc *arc,
const RiseFall *rf) const RiseFall *rf)
{ {
TimingModel *model = arc->model(); TimingModel *model = arc->model();
const GateTableModel *gate_model = dynamic_cast<GateTableModel*>(model); const GateTableModel *gate_model = dynamic_cast<GateTableModel *>(model);
const CheckTableModel *check_model = dynamic_cast<CheckTableModel*>(model); const CheckTableModel *check_model = dynamic_cast<CheckTableModel *>(model);
if (gate_model) { if (gate_model) {
const TableModel *delay_model = gate_model->delayModel(); const TableModel *delay_model = gate_model->delayModel();
const std::string &template_name = delay_model->tblTemplate()->name(); 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); writeTableModel(delay_model);
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
const TableModel *slew_model = gate_model->slewModel(); const TableModel *slew_model = gate_model->slewModel();
if (slew_model) { if (slew_model) {
const std::string &slew_template_name = slew_model->tblTemplate()->name(); 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); writeTableModel(slew_model);
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
} }
} }
else if (check_model) { else if (check_model) {
const TableModel *model = check_model->model(); const TableModel *model = check_model->checkModel();
const std::string &template_name = model->tblTemplate()->name(); 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); writeTableModel(model);
fprintf(stream_, " }\n"); sta::print(stream_, " }}\n");
} }
else else
report_->error(1341, "%s/%s/%s timing model not supported.", report_->error(1341, "{}/{}/{} timing model not supported.", library_->name(),
library_->name(), arc->from()->libertyCell()->name(), arc->from()->name());
arc->from()->libertyCell()->name(),
arc->from()->name());
} }
void void
@ -508,24 +501,23 @@ void
LibertyWriter::writeTableModel0(const TableModel *model) LibertyWriter::writeTableModel0(const TableModel *model)
{ {
float value = model->value(0, 0, 0); float value = model->value(0, 0, 0);
fprintf(stream_, " values(\"%s\");\n", sta::print(stream_, " values(\"{}\");\n", time_unit_->asString(value, 5));
time_unit_->asString(value, 5));
} }
void void
LibertyWriter::writeTableModel1(const TableModel *model) LibertyWriter::writeTableModel1(const TableModel *model)
{ {
writeTableAxis10(model->axis1(), 1); writeTableAxis10(model->axis1(), 1);
fprintf(stream_, " values(\""); sta::print(stream_, " values(\"");
bool first_col = true; bool first_col = true;
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) { for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
float value = model->value(index1, 0, 0); float value = model->value(index1, 0, 0);
if (!first_col) if (!first_col)
fprintf(stream_, ","); sta::print(stream_, ",");
fprintf(stream_, "%s", time_unit_->asString(value, 5)); sta::print(stream_, "{}", time_unit_->asString(value, 5));
first_col = false; first_col = false;
} }
fprintf(stream_, "\");\n"); sta::print(stream_, "\");\n");
} }
void void
@ -533,31 +525,31 @@ LibertyWriter::writeTableModel2(const TableModel *model)
{ {
writeTableAxis10(model->axis1(), 1); writeTableAxis10(model->axis1(), 1);
writeTableAxis10(model->axis2(), 2); writeTableAxis10(model->axis2(), 2);
fprintf(stream_, " values(\""); sta::print(stream_, " values(\"");
bool first_row = true; bool first_row = true;
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) { for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
if (!first_row) { if (!first_row) {
fprintf(stream_, "\\\n"); sta::print(stream_, "\\\n");
fprintf(stream_, " \""); sta::print(stream_, " \"");
} }
bool first_col = true; bool first_col = true;
for (size_t index2 = 0; index2 < model->axis2()->size(); index2++) { for (size_t index2 = 0; index2 < model->axis2()->size(); index2++) {
float value = model->value(index1, index2, 0); float value = model->value(index1, index2, 0);
if (!first_col) if (!first_col)
fprintf(stream_, ","); sta::print(stream_, ",");
fprintf(stream_, "%s", time_unit_->asString(value, 5)); sta::print(stream_, "{}", time_unit_->asString(value, 5));
first_col = false; first_col = false;
} }
fprintf(stream_, "\""); sta::print(stream_, "\"");
first_row = false; first_row = false;
} }
fprintf(stream_, ");\n"); sta::print(stream_, ");\n");
} }
void void
LibertyWriter::writeFooter() LibertyWriter::writeFooter()
{ {
fprintf(stream_, "}\n"); sta::print(stream_, "}}\n");
} }
const char * const char *
@ -571,15 +563,13 @@ LibertyWriter::asString(const PortDirection *dir)
{ {
if (dir == PortDirection::input()) if (dir == PortDirection::input())
return "input"; return "input";
else if (dir == PortDirection::output() else if (dir == PortDirection::output() || (dir == PortDirection::tristate()))
|| (dir == PortDirection::tristate()))
return "output"; return "output";
else if (dir == PortDirection::internal()) else if (dir == PortDirection::internal())
return "internal"; return "internal";
else if (dir == PortDirection::bidirect()) else if (dir == PortDirection::bidirect())
return "inout"; return "inout";
else if (dir == PortDirection::ground() else if (dir == PortDirection::ground() || dir == PortDirection::power())
|| dir == PortDirection::power())
return "input"; return "input";
return "unknown"; return "unknown";
} }
@ -594,8 +584,7 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
return "three_state_disable"; return "three_state_disable";
else if (role == TimingRole::tristateEnable()) else if (role == TimingRole::tristateEnable())
return "three_state_enable"; return "three_state_enable";
else if (role == TimingRole::regClkToQ() else if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) {
|| role == TimingRole::latchEnToQ()) {
const TimingArc *arc = arc_set->arcs()[0]; const TimingArc *arc = arc_set->arcs()[0];
if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
return "rising_edge"; return "rising_edge";
@ -611,16 +600,14 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
else else
return "clear"; return "clear";
} }
else if (role == TimingRole::setup() else if (role == TimingRole::setup() || role == TimingRole::recovery()) {
|| role == TimingRole::recovery()) {
const TimingArc *arc = arc_set->arcs()[0]; const TimingArc *arc = arc_set->arcs()[0];
if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
return "setup_rising"; return "setup_rising";
else else
return "setup_falling"; return "setup_falling";
} }
else if (role == TimingRole::hold() else if (role == TimingRole::hold() || role == TimingRole::removal()) {
|| role == TimingRole::removal()) {
const TimingArc *arc = arc_set->arcs()[0]; const TimingArc *arc = arc_set->arcs()[0];
if (arc->fromEdge()->asRiseFall() == RiseFall::rise()) if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
return "hold_rising"; return "hold_rising";
@ -648,13 +635,11 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
else if (role == TimingRole::width()) else if (role == TimingRole::width())
return "min_pulse_width"; return "min_pulse_width";
else { else {
report_->error(1343, "%s/%s/%s timing arc type %s not supported.", report_->error(1343, "{}/{}/{} timing arc type {} not supported.",
library_->name(), library_->name(), arc_set->to()->libertyCell()->name(),
arc_set->to()->libertyCell()->name(), arc_set->to()->name(), role->to_string());
arc_set->to()->name(),
role->to_string().c_str());
return nullptr; return nullptr;
} }
} }
} // namespace } // namespace sta

View File

@ -42,20 +42,32 @@ void
GateLinearModel::gateDelay(const Pvt *, GateLinearModel::gateDelay(const Pvt *,
float, float,
float load_cap, float load_cap,
bool,
// return values // return values
ArcDelay &gate_delay, float &gate_delay,
Slew &drvr_slew) const float &drvr_slew) const
{ {
gate_delay = intrinsic_ + resistance_ * load_cap; gate_delay = intrinsic_ + resistance_ * load_cap;
drvr_slew = 0.0; drvr_slew = 0.0;
} }
void
GateLinearModel::gateDelayPocv(const Pvt *,
float,
float,
const MinMax *,
PocvMode,
// return values
ArcDelay &,
Slew &) const
{
}
std::string std::string
GateLinearModel::reportGateDelay(const Pvt *, GateLinearModel::reportGateDelay(const Pvt *,
float, float,
float load_cap, float load_cap,
bool, const MinMax *,
PocvMode,
int digits) const int digits) const
{ {
const LibertyLibrary *library = cell_->libertyLibrary(); const LibertyLibrary *library = cell_->libertyLibrary();
@ -98,7 +110,8 @@ CheckLinearModel::checkDelay(const Pvt *,
float, float,
float, float,
float, float,
bool) const const MinMax *,
PocvMode) const
{ {
return intrinsic_; return intrinsic_;
} }
@ -109,7 +122,8 @@ CheckLinearModel::reportCheckDelay(const Pvt *,
const char *, const char *,
float, float,
float, float,
bool, const MinMax *,
PocvMode,
int digits) const int digits) const
{ {
const LibertyLibrary *library = cell_->libertyLibrary(); 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_); GateTimingModel *model = dynamic_cast<GateTimingModel*>(model_);
if (model) { if (model) {
ArcDelay arc_delay; float arc_delay, slew;
Slew slew; model->gateDelay(nullptr, 0.0, 0.0, arc_delay, slew);
model->gateDelay(nullptr, 0.0, 0.0, false, arc_delay, slew);
return arc_delay; return arc_delay;
} }
else else

View File

@ -26,6 +26,7 @@
#include <cmath> // abs #include <cmath> // abs
#include "Format.hh"
#include "StringUtil.hh" #include "StringUtil.hh"
#include "MinMax.hh" // INF #include "MinMax.hh" // INF
#include "Fuzzy.hh" #include "Fuzzy.hh"
@ -127,7 +128,7 @@ Unit::scaleString() const
else if (fuzzyEqual(scale_, 1E-15)) else if (fuzzyEqual(scale_, 1E-15))
return "1f"; return "1f";
else else
return stdstrPrint("%.1e", scale_); return sta::format("{:.1e}", scale_);
} }
std::string std::string
@ -155,19 +156,13 @@ Unit::width() const
return digits_ + 2; return digits_ + 2;
} }
const char * std::string
Unit::asString(float value) const Unit::asString(float value) const
{ {
return asString(value, digits_); return asString(value, digits_);
} }
const char * std::string
Unit::asString(double value) const
{
return asString(static_cast<float>(value), digits_);
}
const char *
Unit::asString(float value, Unit::asString(float value,
int digits) const int digits) const
{ {
@ -179,7 +174,7 @@ Unit::asString(float value,
// prevent "-0.00" on slowaris // prevent "-0.00" on slowaris
if (std::abs(scaled_value) < 1E-6) if (std::abs(scaled_value) < 1E-6)
scaled_value = 0.0; 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) { TEST_F(UnitTest, AsString) {
Unit unit(1e-9f, "s", 3); Unit unit(1e-9f, "s", 3);
const char *str = unit.asString(1e-9f); std::string str = unit.asString(1e-9f);
EXPECT_NE(str, nullptr); EXPECT_FALSE(str.empty());
// Should produce something like "1.000" // Should produce something like "1.000"
} }
@ -1268,8 +1268,8 @@ TEST(InternalPowerTest, DirectConstruction) {
InternalPower pwr(nullptr, nullptr, nullptr, when_expr, models); InternalPower pwr(nullptr, nullptr, nullptr, when_expr, models);
EXPECT_EQ(pwr.when(), when_expr.get()); EXPECT_EQ(pwr.when(), when_expr.get());
EXPECT_EQ(pwr.relatedPgPin(), nullptr); EXPECT_EQ(pwr.relatedPgPin(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::rise()), nullptr); EXPECT_EQ(pwr.model(RiseFall::rise()).model(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr); EXPECT_EQ(pwr.model(RiseFall::fall()).model(), nullptr);
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
@ -1450,26 +1450,27 @@ TEST_F(LinearModelTest, GateLinearModelConstruct) {
TEST_F(LinearModelTest, GateLinearModelGateDelay) { TEST_F(LinearModelTest, GateLinearModelGateDelay) {
GateLinearModel model(cell_, 1.0f, 2.0f); GateLinearModel model(cell_, 1.0f, 2.0f);
ArcDelay gate_delay; float gate_delay;
Slew drvr_slew; float drvr_slew;
// delay = intrinsic + resistance * load_cap = 1.0 + 2.0 * 3.0 = 7.0 // 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); model.gateDelay(nullptr, 0.0f, 3.0f, gate_delay, drvr_slew);
EXPECT_FLOAT_EQ(delayAsFloat(gate_delay), 7.0f); EXPECT_FLOAT_EQ(gate_delay, 7.0f);
EXPECT_FLOAT_EQ(delayAsFloat(drvr_slew), 0.0f); EXPECT_FLOAT_EQ(drvr_slew, 0.0f);
} }
TEST_F(LinearModelTest, GateLinearModelZeroLoad) { TEST_F(LinearModelTest, GateLinearModelZeroLoad) {
GateLinearModel model(cell_, 2.5f, 1.0f); GateLinearModel model(cell_, 2.5f, 1.0f);
ArcDelay gate_delay; float gate_delay;
Slew drvr_slew; float drvr_slew;
// delay = 2.5 + 1.0 * 0.0 = 2.5 // delay = 2.5 + 1.0 * 0.0 = 2.5
model.gateDelay(nullptr, 0.0f, 0.0f, false, gate_delay, drvr_slew); model.gateDelay(nullptr, 0.0f, 0.0f, gate_delay, drvr_slew);
EXPECT_FLOAT_EQ(delayAsFloat(gate_delay), 2.5f); EXPECT_FLOAT_EQ(gate_delay, 2.5f);
} }
TEST_F(LinearModelTest, GateLinearModelReportGateDelay) { TEST_F(LinearModelTest, GateLinearModelReportGateDelay) {
GateLinearModel model(cell_, 1.0f, 2.0f); 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()); EXPECT_FALSE(report.empty());
// Report should contain "Delay =" // Report should contain "Delay ="
EXPECT_NE(report.find("Delay"), std::string::npos); EXPECT_NE(report.find("Delay"), std::string::npos);
@ -1477,23 +1478,27 @@ TEST_F(LinearModelTest, GateLinearModelReportGateDelay) {
TEST_F(LinearModelTest, CheckLinearModelConstruct) { TEST_F(LinearModelTest, CheckLinearModelConstruct) {
CheckLinearModel model(cell_, 3.0f); 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); EXPECT_FLOAT_EQ(delayAsFloat(delay), 3.0f);
} }
TEST_F(LinearModelTest, CheckLinearModelCheckDelay) { TEST_F(LinearModelTest, CheckLinearModelCheckDelay) {
CheckLinearModel model(cell_, 5.5f); CheckLinearModel model(cell_, 5.5f);
// checkDelay always returns intrinsic_ regardless of other params // 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); 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); EXPECT_FLOAT_EQ(delayAsFloat(delay2), 5.5f);
} }
TEST_F(LinearModelTest, CheckLinearModelReportCheckDelay) { TEST_F(LinearModelTest, CheckLinearModelReportCheckDelay) {
CheckLinearModel model(cell_, 2.0f); CheckLinearModel model(cell_, 2.0f);
std::string report = model.reportCheckDelay(nullptr, 0.0f, nullptr, 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_FALSE(report.empty());
EXPECT_NE(report.find("Check"), std::string::npos); EXPECT_NE(report.find("Check"), std::string::npos);
} }
@ -1505,24 +1510,24 @@ TEST_F(LinearModelTest, CheckLinearModelReportCheckDelay) {
TEST(InternalPowerTest, ModelAccess) { TEST(InternalPowerTest, ModelAccess) {
InternalPowerModels models{}; InternalPowerModels models{};
InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models); InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models);
// Initially models should be nullptr // Initially models should have null inner TableModel
EXPECT_EQ(pwr.model(RiseFall::rise()), nullptr); EXPECT_EQ(pwr.model(RiseFall::rise()).model(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr); EXPECT_EQ(pwr.model(RiseFall::fall()).model(), nullptr);
} }
TEST(InternalPowerTest, WithModel) { TEST(InternalPowerTest, WithModel) {
// Create a minimal model: Table -> TableModel -> InternalPowerModel // Create a minimal model: Table -> TableModel -> InternalPowerModel
TablePtr tbl = std::make_shared<Table>(1.0f); TablePtr tbl = std::make_shared<Table>(1.0f);
TableModel *table_model = new TableModel(tbl, nullptr, auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power, ScaleFactorType::internal_power,
RiseFall::rise()); RiseFall::rise());
auto power_model = std::make_shared<InternalPowerModel>(table_model); InternalPowerModel power_model(table_model);
InternalPowerModels models{}; InternalPowerModels models{};
models[RiseFall::riseIndex()] = power_model; models[RiseFall::riseIndex()] = power_model;
InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models); InternalPower pwr(nullptr, nullptr, nullptr, nullptr, models);
EXPECT_EQ(pwr.model(RiseFall::rise()), power_model.get()); EXPECT_NE(pwr.model(RiseFall::rise()).model(), nullptr);
EXPECT_EQ(pwr.model(RiseFall::fall()), nullptr); EXPECT_EQ(pwr.model(RiseFall::fall()).model(), nullptr);
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
@ -3473,7 +3478,7 @@ TEST(LibertyUtilTest, PortLibertyToStaWithBrackets) {
TEST(InternalPowerModelTest, PowerLookupOrder0) { TEST(InternalPowerModelTest, PowerLookupOrder0) {
TablePtr tbl = std::make_shared<Table>(5.0f); TablePtr tbl = std::make_shared<Table>(5.0f);
TableModel *table_model = new TableModel(tbl, nullptr, auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power, ScaleFactorType::internal_power,
RiseFall::rise()); RiseFall::rise());
InternalPowerModel model(table_model); InternalPowerModel model(table_model);
@ -3485,7 +3490,7 @@ TEST(InternalPowerModelTest, PowerLookupOrder0) {
TEST(InternalPowerModelTest, ReportPowerOrder0) { TEST(InternalPowerModelTest, ReportPowerOrder0) {
TablePtr tbl = std::make_shared<Table>(3.0f); TablePtr tbl = std::make_shared<Table>(3.0f);
TableModel *table_model = new TableModel(tbl, nullptr, auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power, ScaleFactorType::internal_power,
RiseFall::rise()); RiseFall::rise());
InternalPowerModel model(table_model); InternalPowerModel model(table_model);
@ -3505,7 +3510,7 @@ TEST(InternalPowerModelTest, PowerLookupOrder1) {
values.push_back(1.0f); values.push_back(1.0f);
values.push_back(3.0f); values.push_back(3.0f);
TablePtr tbl = std::make_shared<Table>(std::move(values), axis); TablePtr tbl = std::make_shared<Table>(std::move(values), axis);
TableModel *table_model = new TableModel(tbl, nullptr, auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power, ScaleFactorType::internal_power,
RiseFall::rise()); RiseFall::rise());
InternalPowerModel model(table_model); InternalPowerModel model(table_model);
@ -3529,7 +3534,7 @@ TEST(InternalPowerModelTest, PowerLookupOrder2) {
FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f); FloatSeq row1; row1.push_back(3.0f); row1.push_back(4.0f);
values.push_back(std::move(row0)); values.push_back(std::move(row1)); values.push_back(std::move(row0)); values.push_back(std::move(row1));
TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2); TablePtr tbl = std::make_shared<Table>(std::move(values), axis1, axis2);
TableModel *table_model = new TableModel(tbl, nullptr, auto table_model = std::make_shared<TableModel>(tbl, nullptr,
ScaleFactorType::internal_power, ScaleFactorType::internal_power,
RiseFall::rise()); RiseFall::rise());
InternalPowerModel model(table_model); InternalPowerModel model(table_model);

View File

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

View File

@ -2003,7 +2003,7 @@ ConcreteNetwork::linkNetwork(const char *top_cell_name,
return top_instance_ != nullptr; return top_instance_ != nullptr;
} }
else { 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; return false;
} }
} }

View File

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

View File

@ -262,24 +262,15 @@ Network::pathName(const Instance *instance) const
{ {
InstanceSeq inst_path; InstanceSeq inst_path;
path(instance, inst_path); path(instance, inst_path);
size_t name_length = 0; std::string path_name;
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';
while (inst_path.size()) { while (inst_path.size()) {
const Instance *inst = inst_path.back(); const Instance *inst = inst_path.back();
const char *inst_name = name(inst); path_name += name(inst);
strcpy(path_ptr, inst_name);
path_ptr += strlen(inst_name);
inst_path.pop_back(); inst_path.pop_back();
if (inst_path.size()) if (!inst_path.empty())
*path_ptr++ = pathDivider(); path_name += pathDivider();
*path_ptr = '\0';
} }
return path_name; return makeTmpString(path_name);
} }
bool bool
@ -376,18 +367,10 @@ Network::pathName(const Pin *pin) const
{ {
const Instance *inst = instance(pin); const Instance *inst = instance(pin);
if (inst && inst != topInstance()) { if (inst && inst != topInstance()) {
const char *inst_name = pathName(inst); std::string path_name = pathName(inst);
size_t inst_name_length = strlen(inst_name); path_name += pathDivider();
const char *port_name = portName(pin); path_name += portName(pin);
size_t port_name_length = strlen(port_name); return makeTmpString(path_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;
} }
else else
return portName(pin); return portName(pin);
@ -464,18 +447,10 @@ Network::pathName(const Net *net) const
{ {
const Instance *inst = instance(net); const Instance *inst = instance(net);
if (inst && inst != topInstance()) { if (inst && inst != topInstance()) {
const char *inst_name = pathName(inst); std::string path_name = pathName(inst);
size_t inst_name_length = strlen(inst_name); path_name += pathDivider();
const char *net_name = name(net); path_name += name(net);
size_t net_name_length = strlen(net_name); return makeTmpString(path_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;
} }
else else
return name(net); return name(net);

View File

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

View File

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

View File

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

View File

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

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