Merge remote-tracking branch 'upstream/master' into sta_update_upstream_lvf_stuff
This commit is contained in:
commit
d3a52be1e9
|
|
@ -39,7 +39,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.14)
|
|||
cmake_policy(SET CMP0086 NEW)
|
||||
endif()
|
||||
|
||||
project(STA VERSION 3.0.0
|
||||
project(STA VERSION 3.0.1
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
|
|
@ -89,13 +89,17 @@ endif()
|
|||
set(STA_SOURCE
|
||||
app/StaMain.cc
|
||||
|
||||
dcalc/ArcDelayCalc.cc
|
||||
dcalc/ArcDcalcWaveforms.cc
|
||||
dcalc/ArcDelayCalc.cc
|
||||
dcalc/ArnoldiDelayCalc.cc
|
||||
dcalc/ArnoldiReduce.cc
|
||||
dcalc/CcsCeffDelayCalc.cc
|
||||
dcalc/Delay.cc
|
||||
dcalc/DelayCalc.cc
|
||||
dcalc/DelayCalcBase.cc
|
||||
dcalc/DelayNormal.cc
|
||||
dcalc/DelayScalar.cc
|
||||
dcalc/DelaySkewNormal.cc
|
||||
dcalc/DmpCeff.cc
|
||||
dcalc/DmpDelayCalc.cc
|
||||
dcalc/FindRoot.cc
|
||||
|
|
@ -106,9 +110,6 @@ set(STA_SOURCE
|
|||
dcalc/PrimaDelayCalc.cc
|
||||
dcalc/UnitDelayCalc.cc
|
||||
|
||||
graph/DelayFloat.cc
|
||||
graph/DelayNormal1.cc
|
||||
graph/DelayNormal2.cc
|
||||
graph/Graph.cc
|
||||
graph/GraphCmp.cc
|
||||
|
||||
|
|
@ -206,6 +207,7 @@ set(STA_SOURCE
|
|||
search/PathEnum.cc
|
||||
search/PathExpanded.cc
|
||||
search/PathGroup.cc
|
||||
search/PocvMode.cc
|
||||
search/Property.cc
|
||||
search/ReportPath.cc
|
||||
search/Search.cc
|
||||
|
|
@ -408,12 +410,18 @@ find_package(Threads)
|
|||
|
||||
find_package(Eigen3 REQUIRED)
|
||||
|
||||
include(cmake/FindCUDD.cmake)
|
||||
|
||||
if("${SSTA}" STREQUAL "")
|
||||
set(SSTA 0)
|
||||
# fmt library: fallback when std::format is not available (e.g. GCC 11 on Ubuntu 22.04)
|
||||
find_package(fmt QUIET)
|
||||
if(NOT fmt_FOUND)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(fmt
|
||||
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
|
||||
GIT_TAG 10.2.1
|
||||
)
|
||||
FetchContent_MakeAvailable(fmt)
|
||||
endif()
|
||||
message(STATUS "SSTA: ${SSTA}")
|
||||
|
||||
include(cmake/FindCUDD.cmake)
|
||||
|
||||
# configure a header file to pass some of the CMake settings
|
||||
configure_file(${STA_HOME}/util/StaConfig.hh.cmake
|
||||
|
|
@ -528,6 +536,7 @@ target_sources(OpenSTA
|
|||
|
||||
target_link_libraries(OpenSTA
|
||||
Eigen3::Eigen
|
||||
fmt::fmt
|
||||
${TCL_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${CUDD_LIB}
|
||||
|
|
@ -561,7 +570,7 @@ endif()
|
|||
|
||||
# common to gcc/clang
|
||||
set(CXX_FLAGS -Wall -Wextra -pedantic -Wcast-qual -Wredundant-decls
|
||||
-Wformat-security -Werror=misleading-indentation)
|
||||
-Wformat-security -Werror=misleading-indentation -Wundef)
|
||||
|
||||
if(ENABLE_TSAN)
|
||||
message(STATUS "Thread sanitizer: ${ENABLE_TSAN}")
|
||||
|
|
|
|||
|
|
@ -96,11 +96,10 @@ sourceTclFile(const char *filename,
|
|||
bool verbose,
|
||||
Tcl_Interp *interp)
|
||||
{
|
||||
std::string cmd;
|
||||
stringPrint(cmd, "sta::include_file %s %s %s",
|
||||
filename,
|
||||
echo ? "1" : "0",
|
||||
verbose ? "1" : "0");
|
||||
std::string cmd = sta::format("sta::include_file {} {} {}",
|
||||
filename,
|
||||
echo ? "1" : "0",
|
||||
verbose ? "1" : "0");
|
||||
int code = Tcl_Eval(interp, cmd.c_str());
|
||||
const char *result = Tcl_GetStringResult(interp);
|
||||
if (result[0] != '\0')
|
||||
|
|
|
|||
|
|
@ -60,8 +60,9 @@ ArcDcalcWaveforms::inputWaveform(ArcDcalcArg &dcalc_arg,
|
|||
bool vdd_exists;
|
||||
library->supplyVoltage("VDD", vdd, vdd_exists);
|
||||
if (!vdd_exists)
|
||||
report->error(1751, "VDD not defined in library %s", library->name());
|
||||
Waveform in_waveform = driver_waveform->waveform(delayAsFloat(in_slew));
|
||||
report->error(1751, "VDD not defined in library {}", library->name());
|
||||
float slew1 = delayAsFloat(in_slew, min_max, sta);
|
||||
Waveform in_waveform = driver_waveform->waveform(slew1);
|
||||
// Delay time axis.
|
||||
FloatSeq time_values;
|
||||
for (float time : in_waveform.axis1()->values())
|
||||
|
|
|
|||
|
|
@ -94,24 +94,24 @@ makeArcDcalcArg(const char *inst_name,
|
|||
else {
|
||||
const Network *network = sta->network();
|
||||
const Instance *inst = network->instance(in_pin);
|
||||
report->warn(2100, "no timing arc for %s input/driver pins.",
|
||||
report->warn(2100, "no timing arc for {} input/driver pins.",
|
||||
network->pathName(inst));
|
||||
}
|
||||
}
|
||||
else
|
||||
report->warn(2101, "%s not a valid rise/fall.", drvr_rf_name);
|
||||
report->warn(2101, "{} not a valid rise/fall.", drvr_rf_name);
|
||||
}
|
||||
else
|
||||
report->warn(2102, "Pin %s/%s not found.", inst_name, drvr_port_name);
|
||||
report->warn(2102, "Pin {}/{} not found.", inst_name, drvr_port_name);
|
||||
}
|
||||
else
|
||||
report->warn(2103, "%s not a valid rise/fall.", in_rf_name);
|
||||
report->warn(2103, "{} not a valid rise/fall.", in_rf_name);
|
||||
}
|
||||
else
|
||||
report->warn(2104, "Pin %s/%s not found.", inst_name, in_port_name);
|
||||
report->warn(2104, "Pin {}/{} not found.", inst_name, in_port_name);
|
||||
}
|
||||
else
|
||||
report->warn(2105, "Instance %s not found.", inst_name);
|
||||
report->warn(2105, "Instance {} not found.", inst_name);
|
||||
return ArcDcalcArg();
|
||||
}
|
||||
|
||||
|
|
@ -257,18 +257,18 @@ ArcDcalcResult::ArcDcalcResult(size_t load_count) :
|
|||
}
|
||||
|
||||
void
|
||||
ArcDcalcResult::setGateDelay(ArcDelay gate_delay)
|
||||
ArcDcalcResult::setGateDelay(const ArcDelay &gate_delay)
|
||||
{
|
||||
gate_delay_ = gate_delay;
|
||||
}
|
||||
|
||||
void
|
||||
ArcDcalcResult::setDrvrSlew(Slew drvr_slew)
|
||||
ArcDcalcResult::setDrvrSlew(const Slew &drvr_slew)
|
||||
{
|
||||
drvr_slew_ = drvr_slew;
|
||||
}
|
||||
|
||||
ArcDelay
|
||||
const ArcDelay &
|
||||
ArcDcalcResult::wireDelay(size_t load_idx) const
|
||||
{
|
||||
return wire_delays_[load_idx];
|
||||
|
|
@ -276,7 +276,7 @@ ArcDcalcResult::wireDelay(size_t load_idx) const
|
|||
|
||||
void
|
||||
ArcDcalcResult::setWireDelay(size_t load_idx,
|
||||
ArcDelay wire_delay)
|
||||
const ArcDelay &wire_delay)
|
||||
{
|
||||
wire_delays_[load_idx] = wire_delay;
|
||||
}
|
||||
|
|
@ -288,7 +288,7 @@ ArcDcalcResult::setLoadCount(size_t load_count)
|
|||
load_slews_.resize(load_count);
|
||||
}
|
||||
|
||||
Slew
|
||||
const Slew &
|
||||
ArcDcalcResult::loadSlew(size_t load_idx) const
|
||||
{
|
||||
return load_slews_[load_idx];
|
||||
|
|
@ -296,7 +296,7 @@ ArcDcalcResult::loadSlew(size_t load_idx) const
|
|||
|
||||
void
|
||||
ArcDcalcResult::setLoadSlew(size_t load_idx,
|
||||
Slew load_slew)
|
||||
const Slew &load_slew)
|
||||
{
|
||||
load_slews_[load_idx] = load_slew;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,7 +237,6 @@ private:
|
|||
ArnoldiReduce *reduce_;
|
||||
delay_work *delay_work_;
|
||||
std::vector<rcmodel*> unsaved_parasitics_;
|
||||
bool pocv_enabled_;
|
||||
};
|
||||
|
||||
ArcDelayCalc *
|
||||
|
|
@ -397,7 +396,6 @@ ArnoldiDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
ConcreteParasitic *cparasitic =
|
||||
reinterpret_cast<ConcreteParasitic*>(const_cast<Parasitic*>(parasitic));
|
||||
rcmodel_ = dynamic_cast<rcmodel*>(cparasitic);
|
||||
pocv_enabled_ = variables_->pocvEnabled();
|
||||
GateTableModel *table_model = arc->gateTableModel(scene, min_max);
|
||||
if (table_model && rcmodel_) {
|
||||
const Pvt *pvt = pinPvt(drvr_pin, scene, min_max);
|
||||
|
|
@ -453,8 +451,8 @@ ArnoldiDelayCalc::gateDelaySlew(const LibertyCell *drvr_cell,
|
|||
auto load_idx_itr = load_pin_index_map.find(load_pin);
|
||||
if (load_idx_itr != load_pin_index_map.end()) {
|
||||
size_t load_idx = load_idx_itr->second;
|
||||
ArcDelay wire_delay = _delayV[i] - _delayV[0];
|
||||
Slew load_slew = _slewV[i];
|
||||
double wire_delay = _delayV[i] - _delayV[0];
|
||||
double load_slew = _slewV[i];
|
||||
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
|
||||
dcalc_result.setWireDelay(load_idx, wire_delay);
|
||||
dcalc_result.setLoadSlew(load_idx, load_slew);
|
||||
|
|
@ -1158,7 +1156,7 @@ ra_hinv(double y,
|
|||
ex = exp(-x);
|
||||
f = x+ex-1.0-y;
|
||||
if (f<-1e-8 || f>1e-8)
|
||||
debugPrint(debug, "arnoldi", 1, "y f %g %g", y, f);
|
||||
debugPrint(debug, "arnoldi", 1, "y f {:g} {:g}", y, f);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
|
@ -1292,7 +1290,7 @@ ArnoldiDelayCalc::ra_solve_for_s(delay_work *D,
|
|||
s = s - f/df;
|
||||
|
||||
if (std::abs(f)>.5e-12) // .5ps
|
||||
debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p %g tlohi %s err %s",
|
||||
debugPrint(debug_, "arnoldi", 1, "ra_solve_for_s p {:g} tlohi {} err {}",
|
||||
p,
|
||||
units_->timeUnit()->asString(tlohi),
|
||||
units_->timeUnit()->asString(f));
|
||||
|
|
@ -1325,9 +1323,8 @@ ArnoldiDelayCalc::ra_get_r(delay_work *D,
|
|||
float c1;
|
||||
double tlohi,r;
|
||||
c1 = ctot;
|
||||
ArcDelay d1;
|
||||
Slew s1;
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, c1, pocv_enabled_, d1, s1);
|
||||
float d1, s1;
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, c1, d1, s1);
|
||||
tlohi = slew_derate*delayAsFloat(s1);
|
||||
r = tlohi/(c_log*c1);
|
||||
if (rdelay>0.0 && r > rdelay)
|
||||
|
|
@ -1346,9 +1343,8 @@ ArnoldiDelayCalc::ra_get_s(delay_work *D,
|
|||
double c_log = con->vlg;
|
||||
double c_smin = con->smin;
|
||||
double tlohi,smin,s;
|
||||
ArcDelay d1;
|
||||
Slew s1;
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, c, pocv_enabled_, d1, s1);
|
||||
float d1, s1;
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, c, d1, s1);
|
||||
tlohi = slew_derate*delayAsFloat(s1);
|
||||
smin = r*c*c_smin; // c_smin = ra_hinv((1-vhi)/vhi-log(vhi)) + log(vhi);
|
||||
if (c_log*r*c >= tlohi) {
|
||||
|
|
@ -1378,10 +1374,9 @@ ArnoldiDelayCalc::ra_rdelay_1(timing_table *tab,
|
|||
float c2 = 0.5*c1;
|
||||
if (c1==c2)
|
||||
return 0.0;
|
||||
ArcDelay d1, d2;
|
||||
Slew s1, s2;
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, c1, pocv_enabled_, d1, s1);
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, c2, pocv_enabled_, d2, s2);
|
||||
float d1, d2, s1, s2;
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, c1, d1, s1);
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, c2, d2, s2);
|
||||
double dt50 = delayAsFloat(d1)-delayAsFloat(d2);
|
||||
if (dt50 <= 0.0)
|
||||
return 0.0;
|
||||
|
|
@ -1402,10 +1397,9 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
|
|||
double vlo = con->vlo;
|
||||
double ctot = mod->ctot;
|
||||
double ceff,tlohi,t50_sy,r,s,t50_sr,rdelay;
|
||||
ArcDelay df;
|
||||
Slew sf;
|
||||
float df, sf;
|
||||
|
||||
debugPrint(debug_, "arnoldi", 1, "ctot=%s",
|
||||
debugPrint(debug_, "arnoldi", 1, "ctot={}",
|
||||
units_->capacitanceUnit()->asString(ctot));
|
||||
|
||||
rdelay = ra_rdelay_1(tab,ctot);
|
||||
|
|
@ -1427,24 +1421,23 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
|
|||
if (debug_->check("arnoldi", 1)) {
|
||||
double p = 1.0/(r*ctot);
|
||||
double thix,tlox;
|
||||
debugPrint(debug_, "arnoldi", 1, "at r=%s s=%s",
|
||||
debugPrint(debug_, "arnoldi", 1, "at r={} s={}",
|
||||
units_->resistanceUnit()->asString(r),
|
||||
units_->timeUnit()->asString(s));
|
||||
thix = ra_solve_for_t(p,s,vhi);
|
||||
tlox = ra_solve_for_t(p,s,vlo);
|
||||
tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, pocv_enabled_, df, sf);
|
||||
debugPrint(debug_, "arnoldi", 1, "table slew (in_slew %s ctot %s) = %s",
|
||||
tab->table->gateDelay(tab->pvt,tab->in_slew, ctot, df, sf);
|
||||
debugPrint(debug_, "arnoldi", 1, "table slew (in_slew {} ctot {}) = {}",
|
||||
units_->timeUnit()->asString(tab->in_slew),
|
||||
units_->capacitanceUnit()->asString(ctot),
|
||||
delayAsString(sf, this));
|
||||
tlohi = slew_derate*delayAsFloat(sf);
|
||||
debugPrint(debug_, "arnoldi", 1, "tlohi %s %s",
|
||||
debugPrint(debug_, "arnoldi", 1, "tlohi {} {}",
|
||||
units_->timeUnit()->asString(tlohi),
|
||||
units_->timeUnit()->asString(tlox-thix));
|
||||
}
|
||||
ceff = ctot;
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, pocv_enabled_,
|
||||
df, sf);
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, df, sf);
|
||||
t50_sy = delayAsFloat(df);
|
||||
t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5);
|
||||
|
||||
|
|
@ -1475,17 +1468,17 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
|
|||
|
||||
// new mvs at ceff
|
||||
s = ra_get_s(D,tab,r,ceff);
|
||||
debugPrint(debug_, "arnoldi", 1, "new mvs s = %s",
|
||||
debugPrint(debug_, "arnoldi", 1, "new mvs s = {}",
|
||||
units_->timeUnit()->asString(s));
|
||||
}
|
||||
}
|
||||
debugPrint(debug_, "arnoldi", 1, "r %s s %s ceff_time %s ceff %s",
|
||||
debugPrint(debug_, "arnoldi", 1, "r {} s {} ceff_time {} ceff {}",
|
||||
units_->resistanceUnit()->asString(r),
|
||||
units_->timeUnit()->asString(s),
|
||||
units_->timeUnit()->asString(ceff_time),
|
||||
units_->capacitanceUnit()->asString(ceff));
|
||||
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, pocv_enabled_, df, sf);
|
||||
tab->table->gateDelay(tab->pvt, tab->in_slew, ceff, df, sf);
|
||||
t50_sy = delayAsFloat(df);
|
||||
t50_sr = ra_solve_for_t(1.0/(r*ceff),s,0.5);
|
||||
for (j=0;j<mod->n;j++) {
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
// (c) 2018 Nefelus, Inc.
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
#include "Network.hh"
|
||||
#include "Units.hh"
|
||||
#include "Arnoldi.hh"
|
||||
#include "Format.hh"
|
||||
#include "parasitics/ConcreteParasiticsPvt.hh"
|
||||
|
||||
namespace sta {
|
||||
|
|
@ -43,10 +44,7 @@ rcmodel::rcmodel() :
|
|||
{
|
||||
}
|
||||
|
||||
rcmodel::~rcmodel()
|
||||
{
|
||||
free(pinV);
|
||||
}
|
||||
rcmodel::~rcmodel() { free(pinV); }
|
||||
|
||||
float
|
||||
rcmodel::capacitance() const
|
||||
|
|
@ -67,7 +65,7 @@ struct ts_point
|
|||
ParasiticNode *node_;
|
||||
int eN;
|
||||
bool is_term;
|
||||
int tindex; // index into termV of corresponding term
|
||||
int tindex; // index into termV of corresponding term
|
||||
ts_edge **eV;
|
||||
bool visited;
|
||||
ts_edge *in_edge;
|
||||
|
|
@ -85,7 +83,6 @@ struct ts_edge
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
const int ArnoldiReduce::ts_point_count_incr_ = 1024;
|
||||
const int ArnoldiReduce::ts_edge_count_incr_ = 1024;
|
||||
|
||||
|
|
@ -96,31 +93,32 @@ ArnoldiReduce::ArnoldiReduce(StaState *sta) :
|
|||
termNmax(256),
|
||||
dNmax(8)
|
||||
{
|
||||
ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point));
|
||||
ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int));
|
||||
ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*));
|
||||
_u0 = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
_u1 = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
y = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
iv = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
r = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
c = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
par = (int*)malloc(ts_pointNmax*sizeof(int));
|
||||
ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point));
|
||||
ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int));
|
||||
ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *));
|
||||
_u0 = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
_u1 = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
y = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
iv = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
r = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
c = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
par = (int *)malloc(ts_pointNmax * sizeof(int));
|
||||
|
||||
ts_edgeV = (ts_edge*)malloc(ts_edgeNmax*sizeof(ts_edge));
|
||||
ts_stackV = (ts_edge**)malloc(ts_edgeNmax*sizeof(ts_edge*));
|
||||
ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*));
|
||||
ts_edgeV = (ts_edge *)malloc(ts_edgeNmax * sizeof(ts_edge));
|
||||
ts_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *));
|
||||
ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *));
|
||||
|
||||
pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*));
|
||||
termV = (int*)malloc(termNmax*sizeof(int));
|
||||
outV = (int*)malloc(termNmax*sizeof(int));
|
||||
pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *));
|
||||
termV = (int *)malloc(termNmax * sizeof(int));
|
||||
outV = (int *)malloc(termNmax * sizeof(int));
|
||||
|
||||
d = (double*)malloc(dNmax*sizeof(double));
|
||||
e = (double*)malloc(dNmax*sizeof(double));
|
||||
U = (double**)malloc(dNmax*sizeof(double*));
|
||||
U0 = (double*)malloc(dNmax*termNmax*sizeof(double));
|
||||
d = (double *)malloc(dNmax * sizeof(double));
|
||||
e = (double *)malloc(dNmax * sizeof(double));
|
||||
U = (double **)malloc(dNmax * sizeof(double *));
|
||||
U0 = (double *)malloc(dNmax * termNmax * sizeof(double));
|
||||
int h;
|
||||
for (h=0;h<dNmax;h++) U[h] = U0 + h*termNmax;
|
||||
for (h = 0; h < dNmax; h++)
|
||||
U[h] = U0 + h * termNmax;
|
||||
}
|
||||
|
||||
ArnoldiReduce::~ArnoldiReduce()
|
||||
|
|
@ -161,7 +159,7 @@ ArnoldiReduce::reduceToArnoldi(Parasitic *parasitic,
|
|||
scene_ = scene;
|
||||
min_max_ = min_max;
|
||||
parasitics_ = scene->parasitics(min_max);
|
||||
parasitic_network_ = reinterpret_cast<ConcreteParasiticNetwork*>(parasitic);
|
||||
parasitic_network_ = reinterpret_cast<ConcreteParasiticNetwork *>(parasitic);
|
||||
|
||||
loadWork();
|
||||
return makeRcmodelDrv();
|
||||
|
|
@ -200,7 +198,7 @@ ArnoldiReduce::loadWork()
|
|||
|
||||
ts_edge *e;
|
||||
int tindex;
|
||||
for (p = p0; p!=pend; p++) {
|
||||
for (p = p0; p != pend; p++) {
|
||||
p->node_ = nullptr;
|
||||
p->eN = 0;
|
||||
p->is_term = false;
|
||||
|
|
@ -246,14 +244,14 @@ ArnoldiReduce::loadWork()
|
|||
e++;
|
||||
}
|
||||
|
||||
for (p=p0;p!=pend;p++) {
|
||||
for (p = p0; p != pend; p++) {
|
||||
if (p->node_) {
|
||||
p->eV = eV;
|
||||
eV += p->eN;
|
||||
p->eN = 0;
|
||||
}
|
||||
}
|
||||
for (e=e0;e!=eend;e++) {
|
||||
for (e = e0; e != eend; e++) {
|
||||
e->from->eV[e->from->eN++] = e;
|
||||
if (e->to != e->from)
|
||||
e->to->eV[e->to->eN++] = e;
|
||||
|
|
@ -267,30 +265,33 @@ ArnoldiReduce::allocPoints()
|
|||
free(par);
|
||||
free(c);
|
||||
free(r);
|
||||
free(iv); free(y); free(_u1); free(_u0);
|
||||
free(iv);
|
||||
free(y);
|
||||
free(_u1);
|
||||
free(_u0);
|
||||
free(ts_pordV);
|
||||
free(ts_ordV);
|
||||
free(ts_pointV);
|
||||
ts_pointNmax = ts_pointN + ts_point_count_incr_;
|
||||
ts_pointV = (ts_point*)malloc(ts_pointNmax*sizeof(ts_point));
|
||||
ts_ordV = (int*)malloc(ts_pointNmax*sizeof(int));
|
||||
ts_pordV = (ts_point**)malloc(ts_pointNmax*sizeof(ts_point*));
|
||||
_u0 = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
_u1 = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
y = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
iv = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
r = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
c = (double*)malloc(ts_pointNmax*sizeof(double));
|
||||
par = (int*)malloc(ts_pointNmax*sizeof(int));
|
||||
ts_pointV = (ts_point *)malloc(ts_pointNmax * sizeof(ts_point));
|
||||
ts_ordV = (int *)malloc(ts_pointNmax * sizeof(int));
|
||||
ts_pordV = (ts_point **)malloc(ts_pointNmax * sizeof(ts_point *));
|
||||
_u0 = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
_u1 = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
y = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
iv = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
r = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
c = (double *)malloc(ts_pointNmax * sizeof(double));
|
||||
par = (int *)malloc(ts_pointNmax * sizeof(int));
|
||||
}
|
||||
if (ts_edgeN > ts_edgeNmax) {
|
||||
free(ts_edgeV);
|
||||
free(ts_eV);
|
||||
free(ts_stackV);
|
||||
ts_edgeNmax = ts_edgeN + ts_edge_count_incr_;
|
||||
ts_edgeV = (ts_edge*)malloc(ts_edgeNmax*sizeof(ts_edge));
|
||||
ts_stackV = (ts_edge**)malloc(ts_edgeNmax*sizeof(ts_edge*));
|
||||
ts_eV = (ts_edge**)malloc(2*ts_edgeNmax*sizeof(ts_edge*));
|
||||
ts_edgeV = (ts_edge *)malloc(ts_edgeNmax * sizeof(ts_edge));
|
||||
ts_stackV = (ts_edge **)malloc(ts_edgeNmax * sizeof(ts_edge *));
|
||||
ts_eV = (ts_edge **)malloc(2 * ts_edgeNmax * sizeof(ts_edge *));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -302,65 +303,69 @@ ArnoldiReduce::allocTerms(int nterms)
|
|||
free(outV);
|
||||
free(termV);
|
||||
free(pinV);
|
||||
termNmax = nterms+256;
|
||||
pinV = (const Pin**)malloc(termNmax*sizeof(const Pin*));
|
||||
termV = (int*)malloc(termNmax*sizeof(int));
|
||||
outV = (int*)malloc(termNmax*sizeof(int));
|
||||
termNmax = nterms + 256;
|
||||
pinV = (const Pin **)malloc(termNmax * sizeof(const Pin *));
|
||||
termV = (int *)malloc(termNmax * sizeof(int));
|
||||
outV = (int *)malloc(termNmax * sizeof(int));
|
||||
|
||||
U0 = (double*)malloc(dNmax*termNmax*sizeof(double));
|
||||
U0 = (double *)malloc(dNmax * termNmax * sizeof(double));
|
||||
int h;
|
||||
for (h=0;h<dNmax;h++) U[h] = U0 + h*termNmax;
|
||||
for (h = 0; h < dNmax; h++)
|
||||
U[h] = U0 + h * termNmax;
|
||||
}
|
||||
}
|
||||
|
||||
ts_point *
|
||||
ArnoldiReduce::findPt(ParasiticNode *node)
|
||||
{
|
||||
return &ts_pointV[pt_map_[reinterpret_cast<ConcreteParasiticNode*>(node)]];
|
||||
return &ts_pointV[pt_map_[reinterpret_cast<ConcreteParasiticNode *>(node)]];
|
||||
}
|
||||
|
||||
rcmodel *
|
||||
ArnoldiReduce::makeRcmodelDrv()
|
||||
{
|
||||
ParasiticNode *drv_node =
|
||||
parasitics_->findParasiticNode(parasitic_network_, drvr_pin_);
|
||||
parasitics_->findParasiticNode(parasitic_network_, drvr_pin_);
|
||||
ts_point *pdrv = findPt(drv_node);
|
||||
makeRcmodelDfs(pdrv);
|
||||
getRC();
|
||||
if (ctot_ < 1e-22) // 1e-10ps
|
||||
if (ctot_ < 1e-22) // 1e-10ps
|
||||
return nullptr;
|
||||
setTerms(pdrv);
|
||||
makeRcmodelFromTs();
|
||||
return makeRcmodelFromW();
|
||||
}
|
||||
|
||||
#define ts_orient( pp, ee) \
|
||||
if (ee->from!=pp) { ee->to = ee->from; ee->from = pp; }
|
||||
#define ts_orient(pp, ee) \
|
||||
if (ee->from != pp) { \
|
||||
ee->to = ee->from; \
|
||||
ee->from = pp; \
|
||||
}
|
||||
|
||||
void
|
||||
ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv)
|
||||
{
|
||||
bool loop = false;
|
||||
int k;
|
||||
ts_point *p,*q;
|
||||
ts_point *p, *q;
|
||||
ts_point *p0 = ts_pointV;
|
||||
ts_point *pend = p0 + ts_pointN;
|
||||
for (p=p0;p!=pend;p++)
|
||||
for (p = p0; p != pend; p++)
|
||||
p->visited = 0;
|
||||
ts_edge *e;
|
||||
|
||||
ts_edge **stackV = ts_stackV;
|
||||
int stackN = 1;
|
||||
stackV[0] = e = pdrv->eV[0];
|
||||
ts_orient(pdrv,e);
|
||||
ts_orient(pdrv, e);
|
||||
pdrv->visited = 1;
|
||||
pdrv->in_edge = nullptr;
|
||||
pdrv->ts = 0;
|
||||
ts_ordV[0] = pdrv-p0;
|
||||
ts_ordV[0] = pdrv - p0;
|
||||
ts_pordV[0] = pdrv;
|
||||
ts_ordN = 1;
|
||||
while (stackN>0) {
|
||||
e = stackV[stackN-1];
|
||||
while (stackN > 0) {
|
||||
e = stackV[stackN - 1];
|
||||
q = e->to;
|
||||
|
||||
if (q->visited) {
|
||||
|
|
@ -368,47 +373,53 @@ ArnoldiReduce::makeRcmodelDfs(ts_point *pdrv)
|
|||
// ignore, and do not even set *loop
|
||||
if (e->to != e->from)
|
||||
loop = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// try to descend
|
||||
q->visited = 1;
|
||||
q->ts = ts_ordN++;
|
||||
ts_pordV[q->ts] = q;
|
||||
ts_ordV[q->ts] = q-p0;
|
||||
ts_ordV[q->ts] = q - p0;
|
||||
q->in_edge = e;
|
||||
if (q->eN>1) {
|
||||
for (k=0;k<q->eN;k++) if (q->eV[k] != e) break;
|
||||
if (q->eN > 1) {
|
||||
for (k = 0; k < q->eN; k++)
|
||||
if (q->eV[k] != e)
|
||||
break;
|
||||
e = q->eV[k];
|
||||
ts_orient(q,e);
|
||||
ts_orient(q, e);
|
||||
stackV[stackN++] = e;
|
||||
continue; // descent
|
||||
continue; // descent
|
||||
}
|
||||
}
|
||||
// try to ascend
|
||||
while (--stackN>=0) {
|
||||
while (--stackN >= 0) {
|
||||
e = stackV[stackN];
|
||||
p = e->from;
|
||||
// find e in p->eV
|
||||
for (k=0;k<p->eN;k++) if (p->eV[k]==e) break;
|
||||
for (k = 0; k < p->eN; k++)
|
||||
if (p->eV[k] == e)
|
||||
break;
|
||||
// if (k==p->eN) notice(0,"ERROR, e not found!\n");
|
||||
++k;
|
||||
if (k>=p->eN) continue;
|
||||
if (k >= p->eN)
|
||||
continue;
|
||||
e = p->eV[k];
|
||||
// check that next sibling is not the incoming edge
|
||||
if (stackN>0 && e==stackV[stackN-1]) {
|
||||
++k;
|
||||
if (k>=p->eN) continue;
|
||||
e = p->eV[k];
|
||||
if (stackN > 0 && e == stackV[stackN - 1]) {
|
||||
++k;
|
||||
if (k >= p->eN)
|
||||
continue;
|
||||
e = p->eV[k];
|
||||
}
|
||||
ts_orient(p,e);
|
||||
ts_orient(p, e);
|
||||
stackV[stackN++] = e;
|
||||
break;
|
||||
}
|
||||
|
||||
} // while (stackN)
|
||||
} // while (stackN)
|
||||
|
||||
if (loop)
|
||||
debugPrint(debug_, "arnoldi", 1, "net %s loop",
|
||||
network_->pathName(drvr_pin_));
|
||||
debugPrint(debug_, "arnoldi", 1, "net {} loop", network_->pathName(drvr_pin_));
|
||||
}
|
||||
|
||||
// makeRcmodelGetRC
|
||||
|
|
@ -418,13 +429,12 @@ ArnoldiReduce::getRC()
|
|||
ts_point *p, *p0 = ts_pointV;
|
||||
ts_point *pend = p0 + ts_pointN;
|
||||
ctot_ = 0.0;
|
||||
for (p=p0;p!=pend;p++) {
|
||||
for (p = p0; p != pend; p++) {
|
||||
p->c = 0.0;
|
||||
p->r = 0.0;
|
||||
if (p->node_) {
|
||||
ParasiticNode *node = p->node_;
|
||||
double cap = parasitics_->nodeGndCap(node)
|
||||
+ pinCapacitance(node);
|
||||
double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
|
||||
if (cap > 0.0) {
|
||||
p->c = cap;
|
||||
ctot_ += cap;
|
||||
|
|
@ -433,11 +443,9 @@ ArnoldiReduce::getRC()
|
|||
p->c = 0.0;
|
||||
if (p->in_edge && p->in_edge->resistor_)
|
||||
p->r = parasitics_->value(p->in_edge->resistor_);
|
||||
if (!(p->r>=0.0 && p->r<100e+3)) { // 0 < r < 100kohm
|
||||
debugPrint(debug_, "arnoldi", 1,
|
||||
"R value %g out of range, drvr pin %s",
|
||||
p->r,
|
||||
network_->pathName(drvr_pin_));
|
||||
if (!(p->r >= 0.0 && p->r < 100e+3)) { // 0 < r < 100kohm
|
||||
debugPrint(debug_, "arnoldi", 1, "R value {:g} out of range, drvr pin {}",
|
||||
p->r, network_->pathName(drvr_pin_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -466,7 +474,7 @@ ArnoldiReduce::pinCapacitance(ParasiticNode *node)
|
|||
LibertyPort *lib_port = network_->libertyPort(port);
|
||||
const Sdc *sdc = scene_->sdc();
|
||||
if (lib_port)
|
||||
pin_cap = sdc->pinCapacitance(pin,rf_, scene_, min_max_);
|
||||
pin_cap = sdc->pinCapacitance(pin, rf_, scene_, min_max_);
|
||||
else if (network_->isTopLevelPort(pin))
|
||||
pin_cap = sdc->portExtCap(port, rf_, min_max_);
|
||||
}
|
||||
|
|
@ -479,13 +487,15 @@ ArnoldiReduce::setTerms(ts_point *pdrv)
|
|||
// termV: from drv-ordered to fixed order
|
||||
// outV: from drv-ordered to ts_pordV
|
||||
ts_point *p;
|
||||
int k,k0;
|
||||
int k, k0;
|
||||
termV[0] = k0 = pdrv->tindex;
|
||||
for (k=1;k<termN;k++) {
|
||||
if (k==k0) termV[k] = 0;
|
||||
else termV[k] = k;
|
||||
for (k = 1; k < termN; k++) {
|
||||
if (k == k0)
|
||||
termV[k] = 0;
|
||||
else
|
||||
termV[k] = k;
|
||||
}
|
||||
for (k=0;k<termN;k++) {
|
||||
for (k = 0; k < termN; k++) {
|
||||
p = pterm0 + termV[k];
|
||||
outV[k] = p->ts;
|
||||
}
|
||||
|
|
@ -498,38 +508,37 @@ ArnoldiReduce::makeRcmodelFromTs()
|
|||
ts_point *p, *p0 = ts_pointV;
|
||||
int n = ts_ordN;
|
||||
int nterms = termN;
|
||||
int i,j,k,h;
|
||||
int i, j, k, h;
|
||||
if (debug_->check("arnoldi", 1)) {
|
||||
for (k=0;k<ts_ordN;k++) {
|
||||
for (k = 0; k < ts_ordN; k++) {
|
||||
p = ts_pordV[k];
|
||||
debugPrint(debug_, "arnoldi", 1, "T%d,P%ld c=%s",
|
||||
p->ts,
|
||||
p-p0,
|
||||
debugPrint(debug_, "arnoldi", 1, "T{} P{} c={}", p->ts, p - p0,
|
||||
units_->capacitanceUnit()->asString(p->c));
|
||||
if (p->is_term)
|
||||
debugPrint(debug_, "arnoldi", 1, " term %d", p->tindex);
|
||||
debugPrint(debug_, "arnoldi", 1, " term {}", p->tindex);
|
||||
if (p->in_edge)
|
||||
debugPrint(debug_, "arnoldi", 1, " from T%d,P%ld r=%s",
|
||||
p->in_edge->from->ts,
|
||||
p->in_edge->from-p0,
|
||||
debugPrint(debug_, "arnoldi", 1, " from T{} P{} r={}",
|
||||
p->in_edge->from->ts, p->in_edge->from - p0,
|
||||
units_->resistanceUnit()->asString(p->r));
|
||||
}
|
||||
for (i=0;i<nterms;i++)
|
||||
debugPrint(debug_, "arnoldi", 1, "outV[%d] = T%d", i, outV[i]);
|
||||
for (i = 0; i < nterms; i++)
|
||||
debugPrint(debug_, "arnoldi", 1, "outV[{}] = T{}", i, outV[i]);
|
||||
}
|
||||
|
||||
int max_order = 5;
|
||||
|
||||
double *u0, *u1;
|
||||
u0 = _u0; u1 = _u1;
|
||||
double sum,e1;
|
||||
u0 = _u0;
|
||||
u1 = _u1;
|
||||
double sum, e1;
|
||||
order = max_order;
|
||||
if (n < order)
|
||||
order = n;
|
||||
|
||||
par[0] = -1; r[0] = 0.0;
|
||||
par[0] = -1;
|
||||
r[0] = 0.0;
|
||||
c[0] = ts_pordV[0]->c;
|
||||
for (j=1;j<n;j++) {
|
||||
for (j = 1; j < n; j++) {
|
||||
p = ts_pordV[j];
|
||||
c[j] = p->c;
|
||||
r[j] = p->r;
|
||||
|
|
@ -537,92 +546,99 @@ ArnoldiReduce::makeRcmodelFromTs()
|
|||
}
|
||||
|
||||
sum = 0.0;
|
||||
for (j=0;j<n;j++) sum += c[j];
|
||||
debugPrint(debug_, "arnoldi", 1, "ctot = %s",
|
||||
for (j = 0; j < n; j++)
|
||||
sum += c[j];
|
||||
debugPrint(debug_, "arnoldi", 1, "ctot = {}",
|
||||
units_->capacitanceUnit()->asString(sum));
|
||||
ctot_ = sum;
|
||||
sqc_ = sqrt(sum);
|
||||
double sqrt_ctot_inv = 1.0/sqc_;
|
||||
for (j=0;j<n;j++) u0[j] = sqrt_ctot_inv;
|
||||
for (h=0;h<order;h++) {
|
||||
for (i=0;i<nterms;i++) U[h][i] = u0[outV[i]];
|
||||
double sqrt_ctot_inv = 1.0 / sqc_;
|
||||
for (j = 0; j < n; j++)
|
||||
u0[j] = sqrt_ctot_inv;
|
||||
for (h = 0; h < order; h++) {
|
||||
for (i = 0; i < nterms; i++)
|
||||
U[h][i] = u0[outV[i]];
|
||||
|
||||
// y = R C u0
|
||||
for (j=0;j<n;j++) {
|
||||
for (j = 0; j < n; j++) {
|
||||
iv[j] = 0.0;
|
||||
}
|
||||
for (j=n-1;j>0;j--) {
|
||||
iv[j] += c[j]*u0[j];
|
||||
for (j = n - 1; j > 0; j--) {
|
||||
iv[j] += c[j] * u0[j];
|
||||
iv[par[j]] += iv[j];
|
||||
}
|
||||
iv[0] += c[0]*u0[0];
|
||||
iv[0] += c[0] * u0[0];
|
||||
y[0] = 0.0;
|
||||
for (j=1;j<n;j++) {
|
||||
y[j] = y[par[j]] + r[j]*iv[j];
|
||||
for (j = 1; j < n; j++) {
|
||||
y[j] = y[par[j]] + r[j] * iv[j];
|
||||
}
|
||||
|
||||
// d[h] = u0 C y
|
||||
sum = 0.0;
|
||||
for (j=1;j<n;j++) {
|
||||
sum += u0[j]*c[j]*y[j];
|
||||
for (j = 1; j < n; j++) {
|
||||
sum += u0[j] * c[j] * y[j];
|
||||
}
|
||||
d[h] = sum;
|
||||
if (h==order-1) break;
|
||||
if (d[h]<1e-13) { // .1ps
|
||||
order = h+1;
|
||||
break;
|
||||
if (h == order - 1)
|
||||
break;
|
||||
if (d[h] < 1e-13) { // .1ps
|
||||
order = h + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// y = y - d[h]*u0 - e[h-1]*u1
|
||||
if (h==0) {
|
||||
for (j=0;j<n;j++) y[j] -= sum*u0[j];
|
||||
} else {
|
||||
e1 = e[h-1];
|
||||
for (j=0;j<n;j++) y[j] -= sum*u0[j] + e1*u1[j];
|
||||
if (h == 0) {
|
||||
for (j = 0; j < n; j++)
|
||||
y[j] -= sum * u0[j];
|
||||
}
|
||||
else {
|
||||
e1 = e[h - 1];
|
||||
for (j = 0; j < n; j++)
|
||||
y[j] -= sum * u0[j] + e1 * u1[j];
|
||||
}
|
||||
|
||||
// e[h] = sqrt(y C y)
|
||||
// u1 = y/e[h]
|
||||
sum = 0.0;
|
||||
for (j=0;j<n;j++) {
|
||||
sum += c[j]*y[j]*y[j];
|
||||
for (j = 0; j < n; j++) {
|
||||
sum += c[j] * y[j] * y[j];
|
||||
}
|
||||
if (sum<1e-30) { // (1e-6ns)^2
|
||||
order = h+1;
|
||||
if (sum < 1e-30) { // (1e-6ns)^2
|
||||
order = h + 1;
|
||||
break;
|
||||
}
|
||||
e[h] = sqrt(sum);
|
||||
sum = 1.0/e[h];
|
||||
for (j=0;j<n;j++) u1[j] = sum*y[j];
|
||||
sum = 1.0 / e[h];
|
||||
for (j = 0; j < n; j++)
|
||||
u1[j] = sum * y[j];
|
||||
|
||||
// swap u0, u1
|
||||
if (h%2) {
|
||||
u0 = _u0; u1 = _u1;
|
||||
} else {
|
||||
u0 = _u1; u1 = _u0;
|
||||
if (h % 2) {
|
||||
u0 = _u0;
|
||||
u1 = _u1;
|
||||
}
|
||||
else {
|
||||
u0 = _u1;
|
||||
u1 = _u0;
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_->check("arnoldi", 1)) {
|
||||
report_->reportLine("tridiagonal reduced matrix, drvr pin %s",
|
||||
network_->pathName(drvr_pin_));
|
||||
report_->reportLine("order %d n %d",order,n);
|
||||
for (h=0;h<order;h++) {
|
||||
if (h<order-1)
|
||||
report_->reportLine(" d[%d] %s e[%d] %s",
|
||||
h,
|
||||
units_->timeUnit()->asString(d[h]),
|
||||
h,
|
||||
units_->timeUnit()->asString(e[h]));
|
||||
report_->report("tridiagonal reduced matrix, drvr pin {}",
|
||||
network_->pathName(drvr_pin_));
|
||||
report_->report("order {} n {}", order, n);
|
||||
for (h = 0; h < order; h++) {
|
||||
if (h < order - 1)
|
||||
report_->report(" d[{}] {} e[{}] {}", h,
|
||||
units_->timeUnit()->asString(d[h]), h,
|
||||
units_->timeUnit()->asString(e[h]));
|
||||
|
||||
else
|
||||
report_->reportLine(" d[%d] %s",
|
||||
h,
|
||||
units_->timeUnit()->asString(d[h]));
|
||||
std::string line = stdstrPrint("U[%d]",h);
|
||||
for (i=0;i<nterms;i++)
|
||||
line += stdstrPrint(" %6.2e",U[h][i]);
|
||||
report_->reportLineString(line);
|
||||
report_->report(" d[{}] {}", h, units_->timeUnit()->asString(d[h]));
|
||||
std::string line = sta::format("U[{}]", h);
|
||||
for (i = 0; i < nterms; i++)
|
||||
line += sta::format(" {:6.2e}", U[h][i]);
|
||||
report_->reportLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -630,29 +646,33 @@ ArnoldiReduce::makeRcmodelFromTs()
|
|||
rcmodel *
|
||||
ArnoldiReduce::makeRcmodelFromW()
|
||||
{
|
||||
int j,h;
|
||||
int j, h;
|
||||
int n = termN;
|
||||
rcmodel *mod = new rcmodel();
|
||||
mod->order = order;
|
||||
mod->n = n;
|
||||
if (order>0) {
|
||||
int totd = order + order - 1 + order*n;
|
||||
mod->d = (double *)malloc(totd*sizeof(double));
|
||||
if (order>1) mod->e = mod->d + order;
|
||||
else mod->e = nullptr;
|
||||
mod->U = (double **)malloc(order*sizeof(double*));
|
||||
if (order > 0) {
|
||||
int totd = order + order - 1 + order * n;
|
||||
mod->d = (double *)malloc(totd * sizeof(double));
|
||||
if (order > 1)
|
||||
mod->e = mod->d + order;
|
||||
else
|
||||
mod->e = nullptr;
|
||||
mod->U = (double **)malloc(order * sizeof(double *));
|
||||
mod->U[0] = mod->d + order + order - 1;
|
||||
for (h=1;h<order;h++) mod->U[h]=mod->U[0] + h*n;
|
||||
for (h=0;h<order;h++) {
|
||||
for (h = 1; h < order; h++)
|
||||
mod->U[h] = mod->U[0] + h * n;
|
||||
for (h = 0; h < order; h++) {
|
||||
mod->d[h] = d[h];
|
||||
if (h<order-1) mod->e[h] = e[h];
|
||||
for (j=0;j<n;j++)
|
||||
if (h < order - 1)
|
||||
mod->e[h] = e[h];
|
||||
for (j = 0; j < n; j++)
|
||||
mod->U[h][j] = U[h][j];
|
||||
}
|
||||
}
|
||||
|
||||
mod->pinV = (const Pin **)malloc(n*sizeof(const Pin*));
|
||||
for (j=0;j<n;j++) {
|
||||
mod->pinV = (const Pin **)malloc(n * sizeof(const Pin *));
|
||||
for (j = 0; j < n; j++) {
|
||||
int k = termV[j];
|
||||
mod->pinV[j] = pinV[k];
|
||||
}
|
||||
|
|
@ -662,4 +682,4 @@ ArnoldiReduce::makeRcmodelFromW()
|
|||
return mod;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "CcsCeffDelayCalc.hh"
|
||||
|
|
@ -63,10 +63,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
|
|||
{
|
||||
}
|
||||
|
||||
CcsCeffDelayCalc::~CcsCeffDelayCalc()
|
||||
{
|
||||
delete table_dcalc_;
|
||||
}
|
||||
CcsCeffDelayCalc::~CcsCeffDelayCalc() { delete table_dcalc_; }
|
||||
|
||||
ArcDelayCalc *
|
||||
CcsCeffDelayCalc::copy()
|
||||
|
|
@ -95,8 +92,8 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
OutputWaveforms *output_waveforms = table_model->outputWaveforms();
|
||||
Parasitics *parasitics = scene->parasitics(min_max);
|
||||
parasitics->piModel(parasitic, c2_, rpi_, c1_);
|
||||
if (output_waveforms
|
||||
&& rpi_ > 0.0 && c1_ > 0.0
|
||||
if (output_waveforms && rpi_ > 0.0
|
||||
&& c1_ > 0.0
|
||||
// Bounds check because extrapolating waveforms does not work for shit.
|
||||
&& output_waveforms->slewAxis()->inBounds(in_slew_)
|
||||
&& output_waveforms->capAxis()->inBounds(c2_)
|
||||
|
|
@ -107,22 +104,20 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
drvr_rf_ = arc->toEdge()->asRiseFall();
|
||||
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
|
||||
if (!vdd_exists)
|
||||
report_->error(1700, "VDD not defined in library %s", drvr_library->name());
|
||||
report_->error(1700, "VDD not defined in library {}", drvr_library->name());
|
||||
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
|
||||
vl_ = drvr_library->slewLowerThreshold(drvr_rf_) * vdd_;
|
||||
vh_ = drvr_library->slewUpperThreshold(drvr_rf_) * vdd_;
|
||||
|
||||
drvr_cell->ensureVoltageWaveforms(scenes_);
|
||||
in_slew_ = delayAsFloat(in_slew);
|
||||
output_waveforms_ = output_waveforms;
|
||||
ref_time_ = output_waveforms_->referenceTime(in_slew_);
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
|
||||
drvr_cell->name(),
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "{} {}", drvr_cell->name(),
|
||||
drvr_rf_->shortName());
|
||||
ArcDelay gate_delay;
|
||||
Slew drvr_slew;
|
||||
double gate_delay, drvr_slew;
|
||||
gateDelaySlew(drvr_library, drvr_rf_, gate_delay, drvr_slew);
|
||||
return makeResult(drvr_library,drvr_rf_,gate_delay,drvr_slew,load_pin_index_map);
|
||||
return makeResult(drvr_library, drvr_rf_, gate_delay, drvr_slew,
|
||||
load_pin_index_map);
|
||||
}
|
||||
}
|
||||
return table_dcalc_->gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
|
||||
|
|
@ -133,8 +128,8 @@ void
|
|||
CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew)
|
||||
double &gate_delay,
|
||||
double &drvr_slew)
|
||||
{
|
||||
initRegions(drvr_library, rf);
|
||||
findCsmWaveform();
|
||||
|
|
@ -142,19 +137,19 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
|
|||
gate_delay = region_times_[region_vth_idx_] - ref_time_;
|
||||
drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"gate_delay %s drvr_slew %s (initial)",
|
||||
"gate_delay {} drvr_slew {} (initial)",
|
||||
delayAsString(gate_delay, this),
|
||||
delayAsString(drvr_slew, this));
|
||||
float prev_drvr_slew = delayAsFloat(drvr_slew);
|
||||
float prev_drvr_slew = drvr_slew;
|
||||
constexpr int max_iterations = 5;
|
||||
for (int iter = 0; iter < max_iterations; iter++) {
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "iteration %d", iter);
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "iteration {}", iter);
|
||||
// Init drvr ramp model for vl.
|
||||
for (size_t i = 0; i <= region_count_; i++) {
|
||||
region_ramp_times_[i] = region_times_[i];
|
||||
if (i < region_count_)
|
||||
region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i])
|
||||
/ (region_times_[i + 1] - region_times_[i]);
|
||||
/ (region_times_[i + 1] - region_times_[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < region_count_; i++) {
|
||||
|
|
@ -174,20 +169,19 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library,
|
|||
double q2 = v2 * c2_ + c1_v2 * c1_;
|
||||
double ceff = (q2 - q1) / (v2 - v1);
|
||||
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "ceff %s",
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "ceff {}",
|
||||
capacitance_unit_->asString(ceff));
|
||||
region_ceff_[i] = ceff;
|
||||
}
|
||||
findCsmWaveform();
|
||||
gate_delay = region_times_[region_vth_idx_] - ref_time_;
|
||||
drvr_slew = std::abs(region_times_[region_vh_idx_] - region_times_[region_vl_idx_]);
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"gate_delay %s drvr_slew %s",
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}",
|
||||
delayAsString(gate_delay, this),
|
||||
delayAsString(drvr_slew, this));
|
||||
if (std::abs(delayAsFloat(drvr_slew) - prev_drvr_slew) < .01 * prev_drvr_slew)
|
||||
if (std::abs(drvr_slew - prev_drvr_slew) < .01 * prev_drvr_slew)
|
||||
break;
|
||||
prev_drvr_slew = delayAsFloat(drvr_slew);
|
||||
prev_drvr_slew = drvr_slew;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,68 +211,68 @@ CcsCeffDelayCalc::initRegions(const LibertyLibrary *drvr_library,
|
|||
|
||||
double vth_vh = (vh_ - vth_);
|
||||
switch (region_count_) {
|
||||
case 4:
|
||||
region_vth_idx_ = 2;
|
||||
region_volts_ = {0.0, vl_, vth_, vh_, vdd_};
|
||||
break;
|
||||
case 5: {
|
||||
region_vth_idx_ = 2;
|
||||
double v1 = vth_ + .7 * vth_vh;
|
||||
region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_};
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
region_vth_idx_ = 2;
|
||||
double v1 = vth_ + .3 * vth_vh;
|
||||
double v2 = vth_ + .6 * vth_vh;
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_};
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 5;
|
||||
double v1 = vth_ + .3 * vth_vh;
|
||||
double v2 = vth_ + .6 * vth_vh;
|
||||
double v3 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_};
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 6;
|
||||
double v1 = vth_ + .25 * vth_vh;
|
||||
double v2 = vth_ + .50 * vth_vh;
|
||||
double v3 = vth_ + .75 * vth_vh;
|
||||
double v4 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_};
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 7;
|
||||
double v1 = vth_ + .2 * vth_vh;
|
||||
double v2 = vth_ + .4 * vth_vh;
|
||||
double v3 = vth_ + .6 * vth_vh;
|
||||
double v4 = vth_ + .8 * vth_vh;
|
||||
double v5 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_};
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 7;
|
||||
double v1 = vth_ + .2 * vth_vh;
|
||||
double v2 = vth_ + .4 * vth_vh;
|
||||
double v3 = vth_ + .6 * vth_vh;
|
||||
double v4 = vth_ + .8 * vth_vh;
|
||||
double v5 = vh_ + .3 * (vdd_ - vh_);
|
||||
double v6 = vh_ + .6 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
report_->error(1701, "unsupported ccs region count.");
|
||||
break;
|
||||
case 4:
|
||||
region_vth_idx_ = 2;
|
||||
region_volts_ = {0.0, vl_, vth_, vh_, vdd_};
|
||||
break;
|
||||
case 5: {
|
||||
region_vth_idx_ = 2;
|
||||
double v1 = vth_ + .7 * vth_vh;
|
||||
region_volts_ = {0.0, vl_, vth_, v1, vh_, vdd_};
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
region_vth_idx_ = 2;
|
||||
double v1 = vth_ + .3 * vth_vh;
|
||||
double v2 = vth_ + .6 * vth_vh;
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, vdd_};
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 5;
|
||||
double v1 = vth_ + .3 * vth_vh;
|
||||
double v2 = vth_ + .6 * vth_vh;
|
||||
double v3 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, vh_, v3, vdd_};
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 6;
|
||||
double v1 = vth_ + .25 * vth_vh;
|
||||
double v2 = vth_ + .50 * vth_vh;
|
||||
double v3 = vth_ + .75 * vth_vh;
|
||||
double v4 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, vh_, v4, vdd_};
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 7;
|
||||
double v1 = vth_ + .2 * vth_vh;
|
||||
double v2 = vth_ + .4 * vth_vh;
|
||||
double v3 = vth_ + .6 * vth_vh;
|
||||
double v4 = vth_ + .8 * vth_vh;
|
||||
double v5 = vh_ + .5 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, vdd_};
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
region_vth_idx_ = 2;
|
||||
region_vh_idx_ = 7;
|
||||
double v1 = vth_ + .2 * vth_vh;
|
||||
double v2 = vth_ + .4 * vth_vh;
|
||||
double v3 = vth_ + .6 * vth_vh;
|
||||
double v4 = vth_ + .8 * vth_vh;
|
||||
double v5 = vh_ + .3 * (vdd_ - vh_);
|
||||
double v6 = vh_ + .6 * (vdd_ - vh_);
|
||||
region_volts_ = {0.0, vl_, vth_, v1, v2, v3, v4, vh_, v5, v6, vdd_};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
report_->error(1701, "unsupported ccs region count.");
|
||||
break;
|
||||
}
|
||||
fill(region_ceff_.begin(), region_ceff_.end(), c2_ + c1_);
|
||||
}
|
||||
|
|
@ -287,15 +281,15 @@ void
|
|||
CcsCeffDelayCalc::findCsmWaveform()
|
||||
{
|
||||
for (size_t i = 0; i < region_count_; i++) {
|
||||
double t1 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
|
||||
region_volts_[i]);
|
||||
double t1 =
|
||||
output_waveforms_->voltageTime(in_slew_, region_ceff_[i], region_volts_[i]);
|
||||
double t2 = output_waveforms_->voltageTime(in_slew_, region_ceff_[i],
|
||||
region_volts_[i + 1]);
|
||||
region_begin_times_[i] = t1;
|
||||
region_end_times_[i] = t2;
|
||||
double time_offset = (i == 0)
|
||||
? 0.0
|
||||
: t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]);
|
||||
? 0.0
|
||||
: t1 - (region_end_times_[i - 1] - region_time_offsets_[i - 1]);
|
||||
region_time_offsets_[i] = time_offset;
|
||||
|
||||
if (i == 0)
|
||||
|
|
@ -309,21 +303,18 @@ CcsCeffDelayCalc::findCsmWaveform()
|
|||
ArcDcalcResult
|
||||
CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew,
|
||||
double &gate_delay,
|
||||
double &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));
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}",
|
||||
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;
|
||||
double wire_delay, 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);
|
||||
|
|
@ -335,23 +326,23 @@ void
|
|||
CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
|
||||
const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
Slew &drvr_slew,
|
||||
double &drvr_slew,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew)
|
||||
double &wire_delay,
|
||||
double &load_slew)
|
||||
{
|
||||
wire_delay = 0.0;
|
||||
load_slew = drvr_slew;
|
||||
|
||||
bool elmore_exists = false;
|
||||
float elmore = 0.0;
|
||||
if (parasitic_
|
||||
&& parasitics_->isPiElmore(parasitic_))
|
||||
if (parasitic_ && parasitics_->isPiElmore(parasitic_))
|
||||
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
|
||||
|
||||
if (elmore_exists &&
|
||||
(elmore == 0.0
|
||||
// Elmore delay is small compared to driver slew.
|
||||
|| elmore < delayAsFloat(drvr_slew) * 1e-3)) {
|
||||
|| elmore < drvr_slew * 1e-3)) {
|
||||
wire_delay = elmore;
|
||||
load_slew = drvr_slew;
|
||||
}
|
||||
|
|
@ -363,17 +354,17 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
|
|||
|
||||
void
|
||||
CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
|
||||
Slew &drvr_slew,
|
||||
double &drvr_slew,
|
||||
float elmore,
|
||||
// Return values.
|
||||
ArcDelay &delay,
|
||||
Slew &slew)
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
for (size_t i = 0; i <= region_count_; i++) {
|
||||
region_ramp_times_[i] = region_times_[i];
|
||||
if (i < region_count_)
|
||||
region_ramp_slopes_[i] = (region_volts_[i + 1] - region_volts_[i])
|
||||
/ (region_times_[i + 1] - region_times_[i]);
|
||||
/ (region_times_[i + 1] - region_times_[i]);
|
||||
}
|
||||
|
||||
vl_fail_ = false;
|
||||
|
|
@ -389,10 +380,8 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
|
|||
slew = drvr_slew;
|
||||
fail("load delay threshold crossing");
|
||||
}
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"load %s delay %s slew %s",
|
||||
network_->pathName(load_pin),
|
||||
delayAsString(delay, this),
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "load {} delay {} slew {}",
|
||||
network_->pathName(load_pin), delayAsString(delay, this),
|
||||
delayAsString(slew, this));
|
||||
}
|
||||
|
||||
|
|
@ -457,12 +446,12 @@ CcsCeffDelayCalc::findVlTime(double v,
|
|||
double t_init = region_ramp_times_[0];
|
||||
double t_final = region_ramp_times_[region_count_];
|
||||
bool root_fail = false;
|
||||
double time = findRoot([&] (double t,
|
||||
double &y,
|
||||
double &dy) {
|
||||
vl(t, elmore, y, dy);
|
||||
y -= v;
|
||||
}, t_init, t_final + elmore * 3.0, .001, 20, root_fail);
|
||||
double time = findRoot(
|
||||
[&](double t, double &y, double &dy) {
|
||||
vl(t, elmore, y, dy);
|
||||
y -= v;
|
||||
},
|
||||
t_init, t_final + elmore * 3.0, .001, 20, root_fail);
|
||||
vl_fail_ |= root_fail;
|
||||
return time;
|
||||
}
|
||||
|
|
@ -487,7 +476,7 @@ PinSeq
|
|||
CcsCeffDelayCalc::watchPins() const
|
||||
{
|
||||
PinSeq pins;
|
||||
for (const auto& [pin, values] : watch_pin_values_)
|
||||
for (const auto &[pin, values] : watch_pin_values_)
|
||||
pins.push_back(pin);
|
||||
return pins;
|
||||
}
|
||||
|
|
@ -523,8 +512,8 @@ CcsCeffDelayCalc::drvrWaveform()
|
|||
drvr_volts->push_back(v);
|
||||
}
|
||||
}
|
||||
TableAxisPtr drvr_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
|
||||
std::move(*drvr_times));
|
||||
TableAxisPtr drvr_time_axis =
|
||||
std::make_shared<TableAxis>(TableAxisVariable::time, std::move(*drvr_times));
|
||||
delete drvr_times;
|
||||
Table drvr_table(drvr_volts, drvr_time_axis);
|
||||
return drvr_table;
|
||||
|
|
@ -555,8 +544,8 @@ CcsCeffDelayCalc::loadWaveform(const Pin *load_pin)
|
|||
double v1 = (drvr_rf_ == RiseFall::rise()) ? v : vdd_ - v;
|
||||
load_volts->push_back(v1);
|
||||
}
|
||||
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
|
||||
std::move(*load_times));
|
||||
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(
|
||||
TableAxisVariable::time, std::move(*load_times));
|
||||
delete load_times;
|
||||
Table load_table(load_volts, load_time_axis);
|
||||
return load_table;
|
||||
|
|
@ -578,10 +567,9 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin,
|
|||
float elmore = 0.0;
|
||||
if (parasitic_) {
|
||||
parasitics_->findElmore(parasitic_, load_pin, elmore, elmore_exists);
|
||||
bool dcalc_success = makeWaveformPreamble(in_pin, in_rf, drvr_pin,
|
||||
drvr_rf, scene, min_max);
|
||||
if (dcalc_success
|
||||
&& elmore_exists) {
|
||||
bool dcalc_success =
|
||||
makeWaveformPreamble(in_pin, in_rf, drvr_pin, drvr_rf, scene, min_max);
|
||||
if (dcalc_success && elmore_exists) {
|
||||
FloatSeq *load_times = new FloatSeq;
|
||||
FloatSeq *load_volts = new FloatSeq;
|
||||
for (size_t j = 0; j <= region_count_; j++) {
|
||||
|
|
@ -600,8 +588,8 @@ CcsCeffDelayCalc::drvrRampWaveform(const Pin *in_pin,
|
|||
double v1 = (drvr_rf == RiseFall::rise()) ? v : vdd_ - v;
|
||||
load_volts->push_back(v1);
|
||||
}
|
||||
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
|
||||
std::move(*load_times));
|
||||
TableAxisPtr load_time_axis = std::make_shared<TableAxis>(
|
||||
TableAxisVariable::time, std::move(*load_times));
|
||||
delete load_times;
|
||||
Table load_table(load_volts, load_time_axis);
|
||||
return load_table;
|
||||
|
|
@ -630,7 +618,7 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin,
|
|||
break;
|
||||
}
|
||||
if (edge) {
|
||||
TimingArc *arc = nullptr;
|
||||
TimingArc *arc = nullptr;
|
||||
for (TimingArc *arc1 : edge->timingArcSet()->arcs()) {
|
||||
if (arc1->fromEdge()->asRiseFall() == in_rf
|
||||
&& arc1->toEdge()->asRiseFall() == drvr_rf) {
|
||||
|
|
@ -645,9 +633,9 @@ CcsCeffDelayCalc::makeWaveformPreamble(const Pin *in_pin,
|
|||
if (parasitic_) {
|
||||
parasitics_->piModel(parasitic_, c2_, rpi_, c1_);
|
||||
LoadPinIndexMap load_pin_index_map =
|
||||
graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
|
||||
gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_,
|
||||
load_pin_index_map, scene, min_max);
|
||||
graph_delay_calc_->makeLoadPinIndexMap(drvr_vertex);
|
||||
gateDelay(drvr_pin, arc, in_slew, load_cap_, parasitic_, load_pin_index_map,
|
||||
scene, min_max);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -671,12 +659,12 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
|
|||
Parasitic *pi_elmore = nullptr;
|
||||
const RiseFall *rf = arc->toEdge()->asRiseFall();
|
||||
if (parasitic && !parasitics_->isPiElmore(parasitic)) {
|
||||
pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf,
|
||||
scene, min_max);
|
||||
pi_elmore =
|
||||
parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, scene, min_max);
|
||||
}
|
||||
std::string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap,
|
||||
pi_elmore, load_pin_index_map,
|
||||
scene, min_max, digits);
|
||||
std::string report =
|
||||
table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, pi_elmore,
|
||||
load_pin_index_map, scene, min_max, digits);
|
||||
parasitics_->deleteDrvrReducedParasitics(drvr_pin);
|
||||
return report;
|
||||
}
|
||||
|
|
@ -686,7 +674,7 @@ CcsCeffDelayCalc::fail(const char *reason)
|
|||
{
|
||||
// Report failures with a unique debug flag.
|
||||
if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1))
|
||||
report_->reportLine("delay_calc: CCS failed - %s", reason);
|
||||
report_->report("delay_calc: CCS failed - {}", reason);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -73,29 +73,29 @@ protected:
|
|||
void gateDelaySlew(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew);
|
||||
double &gate_delay,
|
||||
double &drvr_slew);
|
||||
void initRegions(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf);
|
||||
void findCsmWaveform();
|
||||
ArcDcalcResult makeResult(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew,
|
||||
double &gate_delay,
|
||||
double &drvr_slew,
|
||||
const LoadPinIndexMap &load_pin_index_map);
|
||||
void loadDelaySlew(const Pin *load_pin,
|
||||
const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
Slew &drvr_slew,
|
||||
double &drvr_slew,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew);
|
||||
double &wire_delay,
|
||||
double &load_slew);
|
||||
void loadDelaySlew(const Pin *load_pin,
|
||||
Slew &drvr_slew,
|
||||
double &drvr_slew,
|
||||
float elmore,
|
||||
// Return values.
|
||||
ArcDelay &delay,
|
||||
Slew &slew);
|
||||
double &delay,
|
||||
double &slew);
|
||||
double findVlTime(double v,
|
||||
double elmore);
|
||||
bool makeWaveformPreamble(const Pin *in_pin,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,515 @@
|
|||
// 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,
|
||||
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
|
||||
|
|
@ -131,12 +131,10 @@ proc set_delay_calculator { alg } {
|
|||
if { [is_delay_calc_name $alg] } {
|
||||
set_delay_calculator_cmd $alg
|
||||
} else {
|
||||
sta_error 195 "delay calculator $alg not found."
|
||||
sta_error 2500 "delay calculator $alg not found."
|
||||
}
|
||||
}
|
||||
|
||||
define_cmd_args "set_pocv_sigma_factor" { factor }
|
||||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "set_assigned_delay" \
|
||||
|
|
@ -156,38 +154,38 @@ proc set_assigned_delay { args } {
|
|||
if [info exists keys(-from)] {
|
||||
set from_pins [get_port_pins_error "from_pins" $keys(-from)]
|
||||
} else {
|
||||
sta_error 196 "set_assigned_delay missing -from argument."
|
||||
sta_error 2501 "set_assigned_delay missing -from argument."
|
||||
}
|
||||
if [info exists keys(-to)] {
|
||||
set to_pins [get_port_pins_error "to_pins" $keys(-to)]
|
||||
} else {
|
||||
sta_error 182 "set_assigned_delay missing -to argument."
|
||||
sta_error 2502 "set_assigned_delay missing -to argument."
|
||||
}
|
||||
|
||||
set delay [lindex $args 0]
|
||||
if {![string is double $delay]} {
|
||||
sta_error 183 "set_assigned_delay delay is not a float."
|
||||
sta_error 2503 "set_assigned_delay delay is not a float."
|
||||
}
|
||||
set delay [time_ui_sta $delay]
|
||||
|
||||
if {[info exists flags(-cell)] && [info exists flags(-net)]} {
|
||||
sta_error 184 "set_annotated_delay -cell and -net options are mutually excluive."
|
||||
sta_error 2504 "set_annotated_delay -cell and -net options are mutually excluive."
|
||||
} elseif {[info exists flags(-cell)]} {
|
||||
if { $from_pins != {} } {
|
||||
set inst [[lindex $from_pins 0] instance]
|
||||
foreach pin $from_pins {
|
||||
if {[$pin instance] != $inst} {
|
||||
sta_error 185 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]."
|
||||
sta_error 2505 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]."
|
||||
}
|
||||
}
|
||||
foreach pin $to_pins {
|
||||
if {[$pin instance] != $inst} {
|
||||
sta_error 186 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]"
|
||||
sta_error 2506 "set_assigned_delay pin [get_full_name $pin] is not attached to instance [get_full_name $inst]"
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif {![info exists flags(-net)]} {
|
||||
sta_error 187 "set_assigned_delay -cell or -net required."
|
||||
sta_error 2508 "set_assigned_delay -cell or -net required."
|
||||
}
|
||||
foreach from_pin $from_pins {
|
||||
set from_vertices [$from_pin vertices]
|
||||
|
|
@ -231,7 +229,7 @@ proc set_assigned_delay2 {from_vertex to_vertex to_rf scene min_max delay} {
|
|||
}
|
||||
$edge_iter finish
|
||||
if { !$matched } {
|
||||
sta_error 193 "set_assigned_delay no timing arcs found between from/to pins."
|
||||
sta_error 2509 "set_assigned_delay no timing arcs found between from/to pins."
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +250,7 @@ proc set_assigned_check { args } {
|
|||
if { [info exists keys(-from)] } {
|
||||
set from_pins [get_port_pins_error "from_pins" $keys(-from)]
|
||||
} else {
|
||||
sta_error 188 "set_assigned_check missing -from argument."
|
||||
sta_error 2510 "set_assigned_check missing -from argument."
|
||||
}
|
||||
set from_rf "rise_fall"
|
||||
if { [info exists keys(-clock)] } {
|
||||
|
|
@ -261,14 +259,14 @@ proc set_assigned_check { args } {
|
|||
|| $clk_arg eq "fall" } {
|
||||
set from_rf $clk_arg
|
||||
} else {
|
||||
sta_error 189 "set_assigned_check -clock must be rise or fall."
|
||||
sta_error 2511 "set_assigned_check -clock must be rise or fall."
|
||||
}
|
||||
}
|
||||
|
||||
if { [info exists keys(-to)] } {
|
||||
set to_pins [get_port_pins_error "to_pins" $keys(-to)]
|
||||
} else {
|
||||
sta_error 190 "set_assigned_check missing -to argument."
|
||||
sta_error 2512 "set_assigned_check missing -to argument."
|
||||
}
|
||||
set to_rf [parse_rise_fall_flags flags]
|
||||
set scene [parse_scene keys]
|
||||
|
|
@ -283,7 +281,7 @@ proc set_assigned_check { args } {
|
|||
} elseif { [info exists flags(-removal)] } {
|
||||
set role "removal"
|
||||
} else {
|
||||
sta_error 191 "set_assigned_check missing -setup|-hold|-recovery|-removal check type.."
|
||||
sta_error 2513 "set_assigned_check missing -setup|-hold|-recovery|-removal check type.."
|
||||
}
|
||||
set cond ""
|
||||
if { [info exists key(-cond)] } {
|
||||
|
|
@ -291,7 +289,7 @@ proc set_assigned_check { args } {
|
|||
}
|
||||
set check_value [lindex $args 0]
|
||||
if { ![string is double $check_value] } {
|
||||
sta_error 192 "set_assigned_check check_value is not a float."
|
||||
sta_error 2514 "set_assigned_check check_value is not a float."
|
||||
}
|
||||
set check_value [time_ui_sta $check_value]
|
||||
|
||||
|
|
@ -343,7 +341,7 @@ proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \
|
|||
}
|
||||
$edge_iter finish
|
||||
if { !$matched } {
|
||||
sta_error 194 "set_assigned_check no check arcs found between from/to pins."
|
||||
sta_error 2516 "set_assigned_check no check arcs found between from/to pins."
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +362,7 @@ proc set_assigned_transition { args } {
|
|||
|
||||
set slew [lindex $args 0]
|
||||
if {![string is double $slew]} {
|
||||
sta_error 210 "set_assigned_transition transition is not a float."
|
||||
sta_error 2518 "set_assigned_transition transition is not a float."
|
||||
}
|
||||
set slew [time_ui_sta $slew]
|
||||
set pins [get_port_pins_error "pins" [lindex $args 1]]
|
||||
|
|
@ -382,22 +380,31 @@ proc set_assigned_transition { args } {
|
|||
|
||||
################################################################
|
||||
|
||||
define_cmd_args "report_slews" {[-scenes scenes] pin}
|
||||
define_cmd_args "report_slews" {[-scenes scenes] [-digits digits]\
|
||||
[-report_variance] pin}
|
||||
|
||||
proc report_slews { args } {
|
||||
global sta_report_default_digits
|
||||
|
||||
parse_key_args "report_slews" args keys {-corner -scenes} flags {}
|
||||
parse_key_args "report_slews" args keys {-corner -scenes -digits} \
|
||||
flags {-report_variance}
|
||||
check_argc_eq1 "report_slews" $args
|
||||
|
||||
set scenes [parse_scenes_or_all keys]
|
||||
set pin [get_port_pin_error "pin" [lindex $args 0]]
|
||||
set digits $sta_report_default_digits
|
||||
if [info exists keys(-digits)] {
|
||||
set digits $keys(-digits)
|
||||
check_positive_integer "-digits" $digits
|
||||
} else {
|
||||
set digits $sta_report_default_digits
|
||||
}
|
||||
set report_variance [info exists flags(-report_variance)]
|
||||
|
||||
foreach vertex [$pin vertices] {
|
||||
set rise_min [format_time [$vertex slew_scenes rise $scenes min] $digits]
|
||||
set rise_max [format_time [$vertex slew_scenes rise $scenes max] $digits]
|
||||
set fall_min [format_time [$vertex slew_scenes fall $scenes min] $digits]
|
||||
set fall_max [format_time [$vertex slew_scenes fall $scenes max] $digits]
|
||||
set rise_min [$vertex slew_scenes_string rise $scenes min $report_variance $digits]
|
||||
set rise_max [$vertex slew_scenes_string rise $scenes max $report_variance $digits]
|
||||
set fall_min [$vertex slew_scenes_string fall $scenes min $report_variance $digits]
|
||||
set fall_max [$vertex slew_scenes_string fall $scenes max $report_variance $digits]
|
||||
report_line "[vertex_path_name $vertex] [rise_short_name] $rise_min:$rise_max [fall_short_name] $fall_min:$fall_max"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ DelayCalcBase::finishDrvrPin()
|
|||
void
|
||||
DelayCalcBase::dspfWireDelaySlew(const Pin *load_pin,
|
||||
const RiseFall *rf,
|
||||
Slew drvr_slew,
|
||||
double drvr_slew,
|
||||
float elmore,
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew)
|
||||
double &wire_delay,
|
||||
double &load_slew)
|
||||
{
|
||||
|
||||
LibertyLibrary *load_library = thresholdLibrary(load_pin);
|
||||
|
|
@ -107,8 +107,8 @@ void
|
|||
DelayCalcBase::thresholdAdjust(const Pin *load_pin,
|
||||
const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
ArcDelay &load_delay,
|
||||
Slew &load_slew)
|
||||
double &wire_delay,
|
||||
double &load_slew)
|
||||
{
|
||||
LibertyLibrary *load_library = thresholdLibrary(load_pin);
|
||||
if (load_library
|
||||
|
|
@ -118,11 +118,12 @@ DelayCalcBase::thresholdAdjust(const Pin *load_pin,
|
|||
float load_vth = load_library->inputThreshold(rf);
|
||||
float drvr_slew_delta = drvr_library->slewUpperThreshold(rf)
|
||||
- drvr_library->slewLowerThreshold(rf);
|
||||
float load_delay_delta =
|
||||
float wire_delay_delta =
|
||||
delayAsFloat(load_slew) * ((load_vth - drvr_vth) / drvr_slew_delta);
|
||||
load_delay += (rf == RiseFall::rise())
|
||||
? load_delay_delta
|
||||
: -load_delay_delta;
|
||||
wire_delay += (rf == RiseFall::rise())
|
||||
? wire_delay_delta
|
||||
: -wire_delay_delta;
|
||||
|
||||
float load_slew_delta = load_library->slewUpperThreshold(rf)
|
||||
- load_library->slewLowerThreshold(rf);
|
||||
float drvr_slew_derate = drvr_library->slewDerateFromLibrary();
|
||||
|
|
@ -162,9 +163,8 @@ DelayCalcBase::checkDelay(const Pin *check_pin,
|
|||
float from_slew1 = delayAsFloat(from_slew);
|
||||
float to_slew1 = delayAsFloat(to_slew);
|
||||
return model->checkDelay(pinPvt(check_pin, scene, min_max),
|
||||
from_slew1, to_slew1,
|
||||
related_out_cap,
|
||||
variables_->pocvEnabled());
|
||||
from_slew1, to_slew1, related_out_cap,
|
||||
min_max, variables_->pocvMode());
|
||||
}
|
||||
else
|
||||
return delay_zero;
|
||||
|
|
@ -187,8 +187,8 @@ DelayCalcBase::reportCheckDelay(const Pin *check_pin,
|
|||
float to_slew1 = delayAsFloat(to_slew);
|
||||
return model->reportCheckDelay(pinPvt(check_pin, scene, min_max),
|
||||
from_slew1, from_slew_annotation,
|
||||
to_slew1, related_out_cap, false,
|
||||
digits);
|
||||
to_slew1, related_out_cap, min_max,
|
||||
PocvMode::scalar, digits);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,16 +72,16 @@ protected:
|
|||
void thresholdAdjust(const Pin *load_pin,
|
||||
const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
ArcDelay &load_delay,
|
||||
Slew &load_slew);
|
||||
double &load_delay,
|
||||
double &load_slew);
|
||||
// Helper function for input ports driving dspf parasitic.
|
||||
void dspfWireDelaySlew(const Pin *load_pin,
|
||||
const RiseFall *rf,
|
||||
Slew drvr_slew,
|
||||
double drvr_slew,
|
||||
float elmore,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew);
|
||||
double &wire_delay,
|
||||
double &load_slew);
|
||||
const Pvt *pinPvt(const Pin *pin,
|
||||
const Scene *scene,
|
||||
const MinMax *min_max);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
387
dcalc/DmpCeff.cc
387
dcalc/DmpCeff.cc
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
// "Performance Computation for Precharacterized CMOS Gates with RC Loads",
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
#include <cmath>
|
||||
#include <functional>
|
||||
|
||||
#include "Format.hh"
|
||||
#include "Report.hh"
|
||||
#include "Debug.hh"
|
||||
#include "Units.hh"
|
||||
|
|
@ -90,15 +91,14 @@ gateModelRd(const LibertyCell *cell,
|
|||
double in_slew,
|
||||
double c2,
|
||||
double c1,
|
||||
const Pvt *pvt,
|
||||
bool pocv_enabled);
|
||||
const Pvt *pvt);
|
||||
static void
|
||||
newtonRaphson(const int max_iter,
|
||||
double x[],
|
||||
const int n,
|
||||
const double x_tol,
|
||||
// eval(state) is called to fill fvec and fjac.
|
||||
std::function<void ()> eval,
|
||||
std::function<void()> eval,
|
||||
// Temporaries supplied by caller.
|
||||
double *fvec,
|
||||
double **fjac,
|
||||
|
|
@ -123,7 +123,8 @@ luDecomp(double **a,
|
|||
class DmpAlg : public StaState
|
||||
{
|
||||
public:
|
||||
DmpAlg(int nr_order, StaState *sta);
|
||||
DmpAlg(int nr_order,
|
||||
StaState *sta);
|
||||
~DmpAlg() override = default;
|
||||
virtual const char *name() = 0;
|
||||
// Set driver model and pi model parameters for delay calculation.
|
||||
|
|
@ -137,14 +138,14 @@ public:
|
|||
double c2,
|
||||
double rpi,
|
||||
double c1);
|
||||
virtual void gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew) = 0;
|
||||
virtual void gateDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew) = 0;
|
||||
virtual void loadDelaySlew(const Pin *load_pin,
|
||||
double elmore,
|
||||
// Return values.
|
||||
ArcDelay &delay,
|
||||
Slew &slew);
|
||||
double &delay,
|
||||
double &slew);
|
||||
double ceff() { return ceff_; }
|
||||
|
||||
// Given x_ as a vector of input parameters, fill fvec_ with the
|
||||
|
|
@ -189,9 +190,9 @@ protected:
|
|||
void showX();
|
||||
void showFvec();
|
||||
void showJacobian();
|
||||
void findDriverDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew);
|
||||
void findDriverDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew);
|
||||
double findVoCrossing(double vth,
|
||||
double lower_bound,
|
||||
double upper_bound);
|
||||
|
|
@ -261,7 +262,7 @@ protected:
|
|||
double fjac_storage_[max_nr_order_ * max_nr_order_];
|
||||
double *fjac_[max_nr_order_];
|
||||
double scale_[max_nr_order_];
|
||||
double p_[max_nr_order_ ];
|
||||
double p_[max_nr_order_];
|
||||
int index_[max_nr_order_];
|
||||
|
||||
// Driver slew used to check load delay.
|
||||
|
|
@ -275,7 +276,7 @@ protected:
|
|||
};
|
||||
|
||||
DmpAlg::DmpAlg(int nr_order,
|
||||
StaState *sta):
|
||||
StaState *sta) :
|
||||
StaState(sta),
|
||||
c2_(0.0),
|
||||
rpi_(0.0),
|
||||
|
|
@ -329,14 +330,13 @@ DmpAlg::findDriverParams(double ceff)
|
|||
double t0 = t_vth + std::log(1.0 - vth_) * rd_ * ceff - vth_ * dt;
|
||||
x_[DmpParam::dt] = dt;
|
||||
x_[DmpParam::t0] = t0;
|
||||
newtonRaphson(100, x_, nr_order_, driver_param_tol,
|
||||
[this] () { evalDmpEqns(); },
|
||||
fvec_, fjac_, index_, p_, scale_);
|
||||
newtonRaphson(
|
||||
100, x_, nr_order_, driver_param_tol, [this]() { evalDmpEqns(); }, fvec_,
|
||||
fjac_, index_, p_, scale_);
|
||||
t0_ = x_[DmpParam::t0];
|
||||
dt_ = x_[DmpParam::dt];
|
||||
debugPrint(debug_, "dmp_ceff", 3, " t0 = %s dt = %s ceff = %s",
|
||||
units_->timeUnit()->asString(t0_),
|
||||
units_->timeUnit()->asString(dt_),
|
||||
debugPrint(debug_, "dmp_ceff", 3, " t0 = {} dt = {} ceff = {}",
|
||||
units_->timeUnit()->asString(t0_), units_->timeUnit()->asString(dt_),
|
||||
units_->capacitanceUnit()->asString(x_[DmpParam::ceff]));
|
||||
if (debug_->check("dmp_ceff", 4))
|
||||
showVo();
|
||||
|
|
@ -348,13 +348,10 @@ DmpAlg::gateCapDelaySlew(double ceff,
|
|||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
ArcDelay model_delay;
|
||||
Slew model_slew;
|
||||
gate_model_->gateDelay(pvt_, in_slew_, ceff,
|
||||
variables_->pocvEnabled(),
|
||||
model_delay, model_slew);
|
||||
delay = delayAsFloat(model_delay);
|
||||
slew = delayAsFloat(model_slew);
|
||||
float model_delay, model_slew;
|
||||
gate_model_->gateDelay(pvt_, in_slew_, ceff, model_delay, model_slew);
|
||||
delay = model_delay;
|
||||
slew = model_slew;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -413,8 +410,7 @@ DmpAlg::dy(double t,
|
|||
}
|
||||
else {
|
||||
dydt0 = -(y0dt(t1, cl) - y0dt(t1 - dt, cl)) / dt;
|
||||
dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt)
|
||||
+ y0dt(t1 - dt, cl) / dt;
|
||||
dyddt = -(y0(t1, cl) + y0(t1 - dt, cl)) / (dt * dt) + y0dt(t1 - dt, cl) / dt;
|
||||
dydcl = (y0dcl(t1, cl) - y0dcl(t1 - dt, cl)) / dt;
|
||||
}
|
||||
}
|
||||
|
|
@ -437,14 +433,14 @@ void
|
|||
DmpAlg::showX()
|
||||
{
|
||||
for (int i = 0; i < nr_order_; i++)
|
||||
report_->reportLine("%4s %12.3e", dmp_param_index_strings[i], x_[i]);
|
||||
report_->report("{:4} {:12.3e}", dmp_param_index_strings[i], x_[i]);
|
||||
}
|
||||
|
||||
void
|
||||
DmpAlg::showFvec()
|
||||
{
|
||||
for (int i = 0; i < nr_order_; i++)
|
||||
report_->reportLine("%4s %12.3e", dmp_func_index_strings[i], fvec_[i]);
|
||||
report_->report("{:4} {:12.3e}", dmp_func_index_strings[i], fvec_[i]);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -452,21 +448,21 @@ DmpAlg::showJacobian()
|
|||
{
|
||||
std::string line = " ";
|
||||
for (int j = 0; j < nr_order_; j++)
|
||||
line += stdstrPrint("%12s", dmp_param_index_strings[j]);
|
||||
report_->reportLineString(line);
|
||||
line += sta::format("{:12}", dmp_param_index_strings[j]);
|
||||
report_->reportLine(line);
|
||||
line.clear();
|
||||
for (int i = 0; i < nr_order_; i++) {
|
||||
line += stdstrPrint("%4s ", dmp_func_index_strings[i]);
|
||||
line += sta::format("{:4} ", dmp_func_index_strings[i]);
|
||||
for (int j = 0; j < nr_order_; j++)
|
||||
line += stdstrPrint("%12.3e ", fjac_[i][j]);
|
||||
report_->reportLineString(line);
|
||||
line += sta::format("{:12.3e} ", fjac_[i][j]);
|
||||
report_->reportLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DmpAlg::findDriverDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
DmpAlg::findDriverDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
double t_upper = voCrossingUpperBound();
|
||||
delay = findVoCrossing(vth_, t0_, t_upper);
|
||||
|
|
@ -482,17 +478,15 @@ DmpAlg::findVoCrossing(double vth,
|
|||
double t_lower,
|
||||
double t_upper)
|
||||
{
|
||||
FindRootFunc vo_func = [&] (double t,
|
||||
double &y,
|
||||
double &dy) {
|
||||
FindRootFunc vo_func = [&](double t, double &y, double &dy) {
|
||||
double vo, vo_dt;
|
||||
Vo(t, vo, vo_dt);
|
||||
y = vo - vth;
|
||||
dy = vo_dt;
|
||||
};
|
||||
bool fail;
|
||||
double t_vth = findRoot(vo_func, t_lower, t_upper, vth_time_tol,
|
||||
find_root_max_iter, fail);
|
||||
double t_vth =
|
||||
findRoot(vo_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail);
|
||||
if (fail)
|
||||
throw DmpError("find Vo crossing failed");
|
||||
return t_vth;
|
||||
|
|
@ -514,7 +508,7 @@ DmpAlg::Vo(double t,
|
|||
V0(t1, v0, dv0_dt);
|
||||
|
||||
vo = v0 / dt_;
|
||||
dvo_dt = dv0_dt / dt_;
|
||||
dvo_dt = dv0_dt / dt_;
|
||||
}
|
||||
else {
|
||||
double v0, dv0_dt;
|
||||
|
|
@ -531,20 +525,20 @@ DmpAlg::Vo(double t,
|
|||
void
|
||||
DmpAlg::showVo()
|
||||
{
|
||||
report_->reportLine(" t vo(t)");
|
||||
report_->report(" t vo(t)");
|
||||
double ub = voCrossingUpperBound();
|
||||
for (double t = t0_; t < t0_ + ub; t += dt_ / 10.0) {
|
||||
double vo, dvo_dt;
|
||||
Vo(t, vo, dvo_dt);
|
||||
report_->reportLine(" %g %g", t, vo);
|
||||
report_->report(" {:g} {:g}", t, vo);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DmpAlg::loadDelaySlew(const Pin *,
|
||||
double elmore,
|
||||
ArcDelay &delay,
|
||||
Slew &slew)
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
if (!driver_valid_
|
||||
|| elmore == 0.0
|
||||
|
|
@ -585,8 +579,7 @@ DmpAlg::loadDelaySlew(const Pin *,
|
|||
}
|
||||
delay = delay1;
|
||||
slew = slew1;
|
||||
}
|
||||
catch (DmpError &error) {
|
||||
} catch (DmpError &error) {
|
||||
fail(error.what());
|
||||
delay = elmore_;
|
||||
slew = drvr_slew_;
|
||||
|
|
@ -600,17 +593,15 @@ DmpAlg::findVlCrossing(double vth,
|
|||
double t_lower,
|
||||
double t_upper)
|
||||
{
|
||||
FindRootFunc vl_func = [&] (double t,
|
||||
double &y,
|
||||
double &dy) {
|
||||
FindRootFunc vl_func = [&](double t, double &y, double &dy) {
|
||||
double vl, vl_dt;
|
||||
Vl(t, vl, vl_dt);
|
||||
y = vl - vth;
|
||||
dy = vl_dt;
|
||||
};
|
||||
bool fail;
|
||||
double t_vth = findRoot(vl_func, t_lower, t_upper, vth_time_tol,
|
||||
find_root_max_iter, fail);
|
||||
double t_vth =
|
||||
findRoot(vl_func, t_lower, t_upper, vth_time_tol, find_root_max_iter, fail);
|
||||
if (fail)
|
||||
throw DmpError("find Vl crossing failed");
|
||||
return t_vth;
|
||||
|
|
@ -654,12 +645,12 @@ DmpAlg::Vl(double t,
|
|||
void
|
||||
DmpAlg::showVl()
|
||||
{
|
||||
report_->reportLine(" t vl(t)");
|
||||
report_->report(" t vl(t)");
|
||||
double ub = vlCrossingUpperBound();
|
||||
for (double t = t0_; t < t0_ + ub * 2.0; t += ub / 10.0) {
|
||||
double vl, dvl_dt;
|
||||
Vl(t, vl, dvl_dt);
|
||||
report_->reportLine(" %g %g", t, vl);
|
||||
report_->report(" {:g} {:g}", t, vl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -668,12 +659,11 @@ DmpAlg::fail(const char *reason)
|
|||
{
|
||||
// Report failures with a unique debug flag.
|
||||
if (debug_->check("dmp_ceff", 1) || debug_->check("dcalc_error", 1))
|
||||
report_->reportLine("delay_calc: DMP failed - %s c2=%s rpi=%s c1=%s rd=%s",
|
||||
reason,
|
||||
units_->capacitanceUnit()->asString(c2_),
|
||||
units_->resistanceUnit()->asString(rpi_),
|
||||
units_->capacitanceUnit()->asString(c1_),
|
||||
units_->resistanceUnit()->asString(rd_));
|
||||
report_->report("delay_calc: DMP failed - {} c2={} rpi={} c1={} rd={}", reason,
|
||||
units_->capacitanceUnit()->asString(c2_),
|
||||
units_->resistanceUnit()->asString(rpi_),
|
||||
units_->capacitanceUnit()->asString(c1_),
|
||||
units_->resistanceUnit()->asString(rd_));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -694,14 +684,14 @@ public:
|
|||
double c2,
|
||||
double rpi,
|
||||
double c1) override;
|
||||
void gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew) override;
|
||||
void gateDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew) override;
|
||||
void loadDelaySlew(const Pin *,
|
||||
double elmore,
|
||||
// Return values.
|
||||
ArcDelay &delay,
|
||||
Slew &slew) override;
|
||||
double &delay,
|
||||
double &slew) override;
|
||||
void evalDmpEqns() override;
|
||||
double voCrossingUpperBound() override;
|
||||
|
||||
|
|
@ -716,8 +706,9 @@ private:
|
|||
double &dvl_dt) override;
|
||||
};
|
||||
|
||||
DmpCap::DmpCap(StaState *sta):
|
||||
DmpAlg(1, sta)
|
||||
DmpCap::DmpCap(StaState *sta) :
|
||||
DmpAlg(1,
|
||||
sta)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -734,17 +725,17 @@ DmpCap::init(const LibertyLibrary *drvr_library,
|
|||
double c1)
|
||||
{
|
||||
debugPrint(debug_, "dmp_ceff", 3, "Using DMP cap");
|
||||
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf,
|
||||
rd, in_slew, c2, rpi, c1);
|
||||
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
|
||||
c1);
|
||||
ceff_ = c1 + c2;
|
||||
}
|
||||
|
||||
void
|
||||
DmpCap::gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
DmpCap::gateDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
debugPrint(debug_, "dmp_ceff", 3, " ceff = %s",
|
||||
debugPrint(debug_, "dmp_ceff", 3, " ceff = {}",
|
||||
units_->capacitanceUnit()->asString(ceff_));
|
||||
gateCapDelaySlew(ceff_, delay, slew);
|
||||
drvr_slew_ = slew;
|
||||
|
|
@ -753,8 +744,8 @@ DmpCap::gateDelaySlew(// Return values.
|
|||
void
|
||||
DmpCap::loadDelaySlew(const Pin *,
|
||||
double elmore,
|
||||
ArcDelay &delay,
|
||||
Slew &slew)
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
delay = elmore;
|
||||
slew = drvr_slew_;
|
||||
|
|
@ -782,7 +773,7 @@ DmpCap::voCrossingUpperBound()
|
|||
}
|
||||
|
||||
void
|
||||
DmpCap::Vl0(double ,
|
||||
DmpCap::Vl0(double,
|
||||
// Return values.
|
||||
double &vl,
|
||||
double &dvl_dt)
|
||||
|
|
@ -809,9 +800,9 @@ public:
|
|||
double c2,
|
||||
double rpi,
|
||||
double c1) override;
|
||||
void gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew) override;
|
||||
void gateDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew) override;
|
||||
void evalDmpEqns() override;
|
||||
double voCrossingUpperBound() override;
|
||||
|
||||
|
|
@ -847,7 +838,8 @@ private:
|
|||
};
|
||||
|
||||
DmpPi::DmpPi(StaState *sta) :
|
||||
DmpAlg(3, sta),
|
||||
DmpAlg(3,
|
||||
sta),
|
||||
p1_(0.0),
|
||||
p2_(0.0),
|
||||
z1_(0.0),
|
||||
|
|
@ -875,8 +867,8 @@ DmpPi::init(const LibertyLibrary *drvr_library,
|
|||
double c1)
|
||||
{
|
||||
debugPrint(debug_, "dmp_ceff", 3, "Using DMP Pi");
|
||||
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd,
|
||||
in_slew, c2, rpi, c1);
|
||||
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
|
||||
c1);
|
||||
|
||||
// Find poles/zeros.
|
||||
z1_ = 1.0 / (rpi_ * c1_);
|
||||
|
|
@ -900,9 +892,9 @@ DmpPi::init(const LibertyLibrary *drvr_library,
|
|||
}
|
||||
|
||||
void
|
||||
DmpPi::gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
DmpPi::gateDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
driver_valid_ = false;
|
||||
try {
|
||||
|
|
@ -911,23 +903,21 @@ DmpPi::gateDelaySlew(// Return values.
|
|||
double table_delay, table_slew;
|
||||
gateCapDelaySlew(ceff_, table_delay, table_slew);
|
||||
delay = table_delay;
|
||||
//slew = table_slew;
|
||||
// slew = table_slew;
|
||||
try {
|
||||
double vo_delay, vo_slew;
|
||||
findDriverDelaySlew(vo_delay, vo_slew);
|
||||
driver_valid_ = true;
|
||||
// Save Vo delay to measure load wire delay waveform.
|
||||
vo_delay_ = vo_delay;
|
||||
//delay = vo_delay;
|
||||
// delay = vo_delay;
|
||||
slew = vo_slew;
|
||||
}
|
||||
catch (DmpError &error) {
|
||||
} catch (DmpError &error) {
|
||||
fail(error.what());
|
||||
// Fall back to table slew.
|
||||
slew = table_slew;
|
||||
}
|
||||
}
|
||||
catch (DmpError &error) {
|
||||
} catch (DmpError &error) {
|
||||
fail(error.what());
|
||||
// Driver calculation failed - use Ceff=c1+c2.
|
||||
ceff_ = c1_ + c2_;
|
||||
|
|
@ -941,8 +931,7 @@ DmpPi::findDriverParamsPi()
|
|||
{
|
||||
try {
|
||||
findDriverParams(c2_ + c1_);
|
||||
}
|
||||
catch (DmpError &) {
|
||||
} catch (DmpError &) {
|
||||
findDriverParams(c2_);
|
||||
}
|
||||
}
|
||||
|
|
@ -985,36 +974,33 @@ DmpPi::evalDmpEqns()
|
|||
fvec_[DmpFunc::y20] = y20 - vl_;
|
||||
fjac_[DmpFunc::ipi][DmpParam::t0] = 0.0;
|
||||
fjac_[DmpFunc::ipi][DmpParam::dt] =
|
||||
(-A_ * dt + B_ * dt * exp_p1_dt - (2 * B_ / p1_) * (1.0 - exp_p1_dt)
|
||||
+ D_ * dt * exp_p2_dt - (2 * D_ / p2_) * (1.0 - exp_p2_dt)
|
||||
+ rd_ * ceff * (dt + dt * exp_dt_rd_ceff
|
||||
- 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff)))
|
||||
/ (rd_ * dt * dt * dt);
|
||||
(-A_ * dt + B_ * dt * exp_p1_dt - (2 * B_ / p1_) * (1.0 - exp_p1_dt)
|
||||
+ D_ * dt * exp_p2_dt - (2 * D_ / p2_) * (1.0 - exp_p2_dt)
|
||||
+ rd_ * ceff
|
||||
* (dt + dt * exp_dt_rd_ceff - 2 * rd_ * ceff * (1.0 - exp_dt_rd_ceff)))
|
||||
/ (rd_ * dt * dt * dt);
|
||||
fjac_[DmpFunc::ipi][DmpParam::ceff] =
|
||||
(2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff)))
|
||||
/ (dt * dt);
|
||||
(2 * rd_ * ceff - dt - (2 * rd_ * ceff + dt) * exp2(-dt / (rd_ * ceff)))
|
||||
/ (dt * dt);
|
||||
|
||||
dy(t_vl, t0, dt, ceff,
|
||||
fjac_[DmpFunc::y20][DmpParam::t0],
|
||||
fjac_[DmpFunc::y20][DmpParam::dt],
|
||||
fjac_[DmpFunc::y20][DmpParam::ceff]);
|
||||
dy(t_vl, t0, dt, ceff, fjac_[DmpFunc::y20][DmpParam::t0],
|
||||
fjac_[DmpFunc::y20][DmpParam::dt], fjac_[DmpFunc::y20][DmpParam::ceff]);
|
||||
|
||||
dy(t_vth, t0, dt, ceff,
|
||||
fjac_[DmpFunc::y50][DmpParam::t0],
|
||||
fjac_[DmpFunc::y50][DmpParam::dt],
|
||||
fjac_[DmpFunc::y50][DmpParam::ceff]);
|
||||
dy(t_vth, t0, dt, ceff, fjac_[DmpFunc::y50][DmpParam::t0],
|
||||
fjac_[DmpFunc::y50][DmpParam::dt], fjac_[DmpFunc::y50][DmpParam::ceff]);
|
||||
|
||||
if (debug_->check("dmp_ceff", 4)) {
|
||||
showX();
|
||||
showFvec();
|
||||
showJacobian();
|
||||
report_->reportLine(".................");
|
||||
report_->report(".................");
|
||||
}
|
||||
}
|
||||
|
||||
// Eqn 13, Eqn 14.
|
||||
double
|
||||
DmpPi::ipiIceff(double, double dt,
|
||||
DmpPi::ipiIceff(double,
|
||||
double dt,
|
||||
double ceff_time,
|
||||
double ceff)
|
||||
{
|
||||
|
|
@ -1022,11 +1008,11 @@ DmpPi::ipiIceff(double, double dt,
|
|||
double exp_p2_dt = exp2(-p2_ * ceff_time);
|
||||
double exp_dt_rd_ceff = exp2(-ceff_time / (rd_ * ceff));
|
||||
double ipi = (A_ * ceff_time + (B_ / p1_) * (1.0 - exp_p1_dt)
|
||||
+ (D_ / p2_) * (1.0 - exp_p2_dt))
|
||||
/ (rd_ * ceff_time * dt);
|
||||
double iceff = (rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff)
|
||||
* (1.0 - exp_dt_rd_ceff))
|
||||
/ (rd_ * ceff_time * dt);
|
||||
+ (D_ / p2_) * (1.0 - exp_p2_dt))
|
||||
/ (rd_ * ceff_time * dt);
|
||||
double iceff =
|
||||
(rd_ * ceff * ceff_time - (rd_ * ceff) * (rd_ * ceff) * (1.0 - exp_dt_rd_ceff))
|
||||
/ (rd_ * ceff_time * dt);
|
||||
return ipi - iceff;
|
||||
}
|
||||
|
||||
|
|
@ -1051,14 +1037,13 @@ DmpPi::Vl0(double t,
|
|||
double D1 = k0_ * (k1_ - k2_ / p3_);
|
||||
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
|
||||
double D4 = -p3_ * k0_ * k4_ / (p2_ - p3_);
|
||||
double D5 = k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_)
|
||||
+ p3_ * k4_ / (p2_ - p3_));
|
||||
double D5 =
|
||||
k0_ * (k2_ / p3_ - k1_ + p3_ * k3_ / (p1_ - p3_) + p3_ * k4_ / (p2_ - p3_));
|
||||
double exp_p1 = exp2(-p1_ * t);
|
||||
double exp_p2 = exp2(-p2_ * t);
|
||||
double exp_p3 = exp2(-p3_ * t);
|
||||
vl = D1 + t + D3 * exp_p1 + D4 * exp_p2 + D5 * exp_p3;
|
||||
dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2
|
||||
- D5 * p3_ * exp_p3;
|
||||
dvl_dt = 1.0 - D3 * p1_ * exp_p1 - D4 * p2_ * exp_p2 - D5 * p3_ * exp_p3;
|
||||
}
|
||||
|
||||
double
|
||||
|
|
@ -1080,7 +1065,8 @@ public:
|
|||
};
|
||||
|
||||
DmpOnePole::DmpOnePole(StaState *sta) :
|
||||
DmpAlg(2, sta)
|
||||
DmpAlg(2,
|
||||
sta)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -1104,19 +1090,15 @@ DmpOnePole::evalDmpEqns()
|
|||
showFvec();
|
||||
}
|
||||
|
||||
dy(t_vl, t0, dt, ceff_,
|
||||
fjac_[DmpFunc::y20][DmpParam::t0],
|
||||
fjac_[DmpFunc::y20][DmpParam::dt],
|
||||
ignore2);
|
||||
dy(t_vl, t0, dt, ceff_, fjac_[DmpFunc::y20][DmpParam::t0],
|
||||
fjac_[DmpFunc::y20][DmpParam::dt], ignore2);
|
||||
|
||||
dy(t_vth, t0, dt, ceff_,
|
||||
fjac_[DmpFunc::y50][DmpParam::t0],
|
||||
fjac_[DmpFunc::y50][DmpParam::dt],
|
||||
ignore2);
|
||||
dy(t_vth, t0, dt, ceff_, fjac_[DmpFunc::y50][DmpParam::t0],
|
||||
fjac_[DmpFunc::y50][DmpParam::dt], ignore2);
|
||||
|
||||
if (debug_->check("dmp_ceff", 4)) {
|
||||
showJacobian();
|
||||
report_->reportLine(".................");
|
||||
report_->report(".................");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1144,19 +1126,19 @@ public:
|
|||
double c2,
|
||||
double rpi,
|
||||
double c1) override;
|
||||
void gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew) override;
|
||||
void gateDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew) override;
|
||||
|
||||
private:
|
||||
void V0(double t,
|
||||
// Return values.
|
||||
double &vo,
|
||||
double &dvo_dt) override;
|
||||
void Vl0(double t,
|
||||
// Return values.
|
||||
double &vl,
|
||||
double &dvl_dt) override;
|
||||
void Vl0(double t,
|
||||
// Return values.
|
||||
double &vl,
|
||||
double &dvl_dt) override;
|
||||
double voCrossingUpperBound() override;
|
||||
|
||||
// Pole/zero.
|
||||
|
|
@ -1193,8 +1175,8 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library,
|
|||
double c1)
|
||||
{
|
||||
debugPrint(debug_, "dmp_ceff", 3, "Using DMP C2=0");
|
||||
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd,
|
||||
in_slew, c2, rpi, c1);
|
||||
DmpAlg::init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
|
||||
c1);
|
||||
ceff_ = c1;
|
||||
|
||||
z1_ = 1.0 / (rpi_ * c1_);
|
||||
|
|
@ -1207,9 +1189,9 @@ DmpZeroC2::init(const LibertyLibrary *drvr_library,
|
|||
}
|
||||
|
||||
void
|
||||
DmpZeroC2::gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
DmpZeroC2::gateDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
try {
|
||||
findDriverParams(c1_);
|
||||
|
|
@ -1217,8 +1199,7 @@ DmpZeroC2::gateDelaySlew(// Return values.
|
|||
findDriverDelaySlew(delay, slew);
|
||||
driver_valid_ = true;
|
||||
vo_delay_ = delay;
|
||||
}
|
||||
catch (DmpError &error) {
|
||||
} catch (DmpError &error) {
|
||||
fail(error.what());
|
||||
// Fall back to table slew.
|
||||
driver_valid_ = false;
|
||||
|
|
@ -1241,9 +1222,9 @@ DmpZeroC2::V0(double t,
|
|||
|
||||
void
|
||||
DmpZeroC2::Vl0(double t,
|
||||
// Return values.
|
||||
double &vl,
|
||||
double &dvl_dt)
|
||||
// Return values.
|
||||
double &vl,
|
||||
double &dvl_dt)
|
||||
{
|
||||
double D1 = k0_ * (k1_ - k2_ / p3_);
|
||||
double D3 = -p3_ * k0_ * k3_ / (p1_ - p3_);
|
||||
|
|
@ -1271,7 +1252,7 @@ newtonRaphson(const int max_iter,
|
|||
double x[],
|
||||
const int size,
|
||||
const double x_tol,
|
||||
std::function<void ()> eval,
|
||||
std::function<void()> eval,
|
||||
// Temporaries supplied by caller.
|
||||
double *fvec,
|
||||
double **fjac,
|
||||
|
|
@ -1498,32 +1479,48 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
parasitics_->piModel(parasitic, c2, rpi, c1);
|
||||
if (std::isnan(c2) || std::isnan(c1) || std::isnan(rpi))
|
||||
report_->error(1040, "parasitic Pi model has NaNs.");
|
||||
setCeffAlgorithm(drvr_library, drvr_cell, pinPvt(drvr_pin, scene, min_max),
|
||||
const Pvt *pvt = pinPvt(drvr_pin, scene, min_max);
|
||||
setCeffAlgorithm(drvr_library, drvr_cell, pvt,
|
||||
table_model, rf, in_slew1, c2, rpi, c1);
|
||||
double gate_delay, drvr_slew;
|
||||
gateDelaySlew(gate_delay, drvr_slew);
|
||||
|
||||
// Fill in pocv parameters.
|
||||
double ceff = dmp_alg_->ceff();
|
||||
ArcDelay gate_delay2(gate_delay);
|
||||
Slew drvr_slew2(drvr_slew);
|
||||
if (variables_->pocvEnabled())
|
||||
table_model->gateDelayPocv(pvt, in_slew1, ceff, min_max,
|
||||
variables_->pocvMode(),
|
||||
gate_delay2, drvr_slew2);
|
||||
ArcDcalcResult dcalc_result(load_pin_index_map.size());
|
||||
dcalc_result.setGateDelay(gate_delay);
|
||||
dcalc_result.setDrvrSlew(drvr_slew);
|
||||
dcalc_result.setGateDelay(gate_delay2);
|
||||
dcalc_result.setDrvrSlew(drvr_slew2);
|
||||
|
||||
for (const auto &[load_pin, load_idx] : load_pin_index_map) {
|
||||
ArcDelay wire_delay;
|
||||
Slew load_slew;
|
||||
double wire_delay;
|
||||
double load_slew;
|
||||
loadDelaySlew(load_pin, drvr_slew, rf, drvr_library, parasitic,
|
||||
wire_delay, load_slew);
|
||||
dcalc_result.setWireDelay(load_idx, wire_delay);
|
||||
dcalc_result.setLoadSlew(load_idx, load_slew);
|
||||
// Copy pocv params from driver.
|
||||
ArcDelay wire_delay2(gate_delay2);
|
||||
Slew load_slew2(drvr_slew2);
|
||||
delaySetMean(wire_delay2, wire_delay);
|
||||
delaySetMean(load_slew2, load_slew);
|
||||
dcalc_result.setWireDelay(load_idx, wire_delay2);
|
||||
dcalc_result.setLoadSlew(load_idx, load_slew2);
|
||||
}
|
||||
return dcalc_result;
|
||||
}
|
||||
else {
|
||||
ArcDcalcResult dcalc_result =
|
||||
LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
|
||||
load_pin_index_map, scene, min_max);
|
||||
if (parasitic
|
||||
&& !unsuppored_model_warned_) {
|
||||
LumpedCapDelayCalc::gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic,
|
||||
load_pin_index_map, scene, min_max);
|
||||
if (parasitic && !unsuppored_model_warned_) {
|
||||
unsuppored_model_warned_ = true;
|
||||
report_->warn(1041, "cell %s delay model not supported on SPF parasitics by DMP delay calculator",
|
||||
report_->warn(1041,
|
||||
"cell {} delay model not supported on SPF parasitics by DMP "
|
||||
"delay calculator",
|
||||
drvr_cell->name());
|
||||
}
|
||||
return dcalc_result;
|
||||
|
|
@ -1543,8 +1540,7 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library,
|
|||
{
|
||||
double rd = 0.0;
|
||||
if (gate_model) {
|
||||
rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1,
|
||||
pvt, variables_->pocvEnabled());
|
||||
rd = gateModelRd(drvr_cell, gate_model, rf, in_slew, c2, c1, pvt);
|
||||
// Zero Rd means the table is constant and thus independent of load cap.
|
||||
if (rd < 1e-2
|
||||
// Rpi is small compared to Rd, which makes the load capacitive.
|
||||
|
|
@ -1560,16 +1556,15 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library,
|
|||
}
|
||||
else
|
||||
dmp_alg_ = dmp_cap_;
|
||||
dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model,
|
||||
rf, rd, in_slew, c2, rpi, c1);
|
||||
dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew, c2, rpi,
|
||||
c1);
|
||||
debugPrint(debug_, "dmp_ceff", 3,
|
||||
" DMP in_slew = %s c2 = %s rpi = %s c1 = %s Rd = %s (%s alg)",
|
||||
" DMP in_slew = {} c2 = {} rpi = {} c1 = {} Rd = {} ({} alg)",
|
||||
units_->timeUnit()->asString(in_slew),
|
||||
units_->capacitanceUnit()->asString(c2),
|
||||
units_->resistanceUnit()->asString(rpi),
|
||||
units_->capacitanceUnit()->asString(c1),
|
||||
units_->resistanceUnit()->asString(rd),
|
||||
dmp_alg_->name());
|
||||
units_->resistanceUnit()->asString(rd), dmp_alg_->name());
|
||||
}
|
||||
|
||||
std::string
|
||||
|
|
@ -1583,8 +1578,9 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
|
|||
const MinMax *min_max,
|
||||
int digits)
|
||||
{
|
||||
ArcDcalcResult dcalc_result = gateDelay(drvr_pin, arc, in_slew, load_cap,
|
||||
parasitic, load_pin_index_map, scene, min_max);
|
||||
ArcDcalcResult dcalc_result =
|
||||
gateDelay(drvr_pin, arc, in_slew, load_cap, parasitic, load_pin_index_map,
|
||||
scene, min_max);
|
||||
GateTableModel *model = arc->gateTableModel(scene, min_max);
|
||||
float c_eff = 0.0;
|
||||
std::string result;
|
||||
|
|
@ -1612,14 +1608,12 @@ DmpCeffDelayCalc::reportGateDelay(const Pin *drvr_pin,
|
|||
else
|
||||
c_eff = load_cap;
|
||||
if (model) {
|
||||
const Unit *time_unit = units->timeUnit();
|
||||
float in_slew1 = delayAsFloat(in_slew);
|
||||
result += model->reportGateDelay(pinPvt(drvr_pin, scene, min_max),
|
||||
in_slew1, c_eff,
|
||||
variables_->pocvEnabled(), digits);
|
||||
in_slew1, c_eff, min_max,
|
||||
variables_->pocvMode(), digits);
|
||||
result += "Driver waveform slew = ";
|
||||
float drvr_slew = delayAsFloat(dcalc_result.drvrSlew());
|
||||
result += time_unit->asString(drvr_slew, digits);
|
||||
result += delayAsString(dcalc_result.drvrSlew(), min_max, digits, this);
|
||||
result += '\n';
|
||||
}
|
||||
return result;
|
||||
|
|
@ -1632,25 +1626,22 @@ gateModelRd(const LibertyCell *cell,
|
|||
double in_slew,
|
||||
double c2,
|
||||
double c1,
|
||||
const Pvt *pvt,
|
||||
bool pocv_enabled)
|
||||
const Pvt *pvt)
|
||||
{
|
||||
float cap1 = c1 + c2;
|
||||
float cap2 = cap1 + 1e-15;
|
||||
ArcDelay d1, d2;
|
||||
Slew s1, s2;
|
||||
gate_model->gateDelay(pvt, in_slew, cap1, pocv_enabled, d1, s1);
|
||||
gate_model->gateDelay(pvt, in_slew, cap2, pocv_enabled, d2, s2);
|
||||
float d1, d2, s1, s2;
|
||||
gate_model->gateDelay(pvt, in_slew, cap1, d1, s1);
|
||||
gate_model->gateDelay(pvt, in_slew, cap2, d2, s2);
|
||||
double vth = cell->libertyLibrary()->outputThreshold(rf);
|
||||
float rd = -std::log(vth) * std::abs(delayAsFloat(d1) - delayAsFloat(d2))
|
||||
/ (cap2 - cap1);
|
||||
float rd = -std::log(vth) * std::abs(d1 - d2) / (cap2 - cap1);
|
||||
return rd;
|
||||
}
|
||||
|
||||
void
|
||||
DmpCeffDelayCalc::gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
DmpCeffDelayCalc::gateDelaySlew( // Return values.
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
dmp_alg_->gateDelaySlew(delay, slew);
|
||||
}
|
||||
|
|
@ -1658,8 +1649,8 @@ DmpCeffDelayCalc::gateDelaySlew(// Return values.
|
|||
void
|
||||
DmpCeffDelayCalc::loadDelaySlewElmore(const Pin *load_pin,
|
||||
double elmore,
|
||||
ArcDelay &delay,
|
||||
Slew &slew)
|
||||
double &delay,
|
||||
double &slew)
|
||||
{
|
||||
if (dmp_alg_)
|
||||
dmp_alg_->loadDelaySlew(load_pin, elmore, delay, slew);
|
||||
|
|
@ -1678,7 +1669,7 @@ DmpCeffDelayCalc::copyState(const StaState *sta)
|
|||
DmpError::DmpError(const char *what) :
|
||||
what_(what)
|
||||
{
|
||||
//printf("DmpError %s\n", what);
|
||||
// printf("DmpError %s\n", what);
|
||||
}
|
||||
|
||||
// This saves about 2.5% in overall run time on designs with SPEF.
|
||||
|
|
@ -1707,4 +1698,4 @@ exp2(double x)
|
|||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -69,15 +69,15 @@ protected:
|
|||
const LibertyLibrary *drvr_library,
|
||||
const Parasitic *parasitic,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew) = 0;
|
||||
double &wire_delay,
|
||||
double &load_slew) = 0;
|
||||
void gateDelaySlew(// Return values.
|
||||
double &delay,
|
||||
double &slew);
|
||||
void loadDelaySlewElmore(const Pin *load_pin,
|
||||
double elmore,
|
||||
ArcDelay &delay,
|
||||
Slew &slew);
|
||||
double &delay,
|
||||
double &slew);
|
||||
// Select the appropriate special case Dartu/Menezes/Pileggi algorithm.
|
||||
void setCeffAlgorithm(const LibertyLibrary *library,
|
||||
const LibertyCell *cell,
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ protected:
|
|||
const LibertyLibrary *drvr_library,
|
||||
const Parasitic *parasitic,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew) override;
|
||||
double &wire_delay,
|
||||
double &load_slew) override;
|
||||
};
|
||||
|
||||
ArcDelayCalc *
|
||||
|
|
@ -93,8 +93,8 @@ DmpCeffElmoreDelayCalc::inputPortDelay(const Pin *,
|
|||
ArcDcalcResult dcalc_result(load_pin_index_map.size());
|
||||
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
|
||||
for (auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
ArcDelay wire_delay = 0.0;
|
||||
Slew load_slew = in_slew;
|
||||
double wire_delay = 0.0;
|
||||
double load_slew = in_slew;
|
||||
bool elmore_exists = false;
|
||||
float elmore = 0.0;
|
||||
if (parasitic)
|
||||
|
|
@ -116,8 +116,8 @@ DmpCeffElmoreDelayCalc::loadDelaySlew(const Pin *load_pin,
|
|||
const LibertyLibrary *drvr_library,
|
||||
const Parasitic *parasitic,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew)
|
||||
double &wire_delay,
|
||||
double &load_slew)
|
||||
{
|
||||
wire_delay = 0.0;
|
||||
load_slew = drvr_slew;
|
||||
|
|
@ -143,14 +143,14 @@ public:
|
|||
Parasitic *findParasitic(const Pin *drvr_pin,
|
||||
const RiseFall *rf,
|
||||
const Scene *scene,
|
||||
const MinMax *min_max) override;
|
||||
const MinMax *min_max) override;
|
||||
ArcDcalcResult inputPortDelay(const Pin *port_pin,
|
||||
float in_slew,
|
||||
const RiseFall *rf,
|
||||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const Scene *scene,
|
||||
const MinMax *min_max) override;
|
||||
const MinMax *min_max) override;
|
||||
ArcDcalcResult gateDelay(const Pin *drvr_pin,
|
||||
const TimingArc *arc,
|
||||
const Slew &in_slew,
|
||||
|
|
@ -158,7 +158,7 @@ public:
|
|||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const Scene *scene,
|
||||
const MinMax *min_max) override;
|
||||
const MinMax *min_max) override;
|
||||
|
||||
private:
|
||||
void loadDelaySlew(const Pin *load_pin,
|
||||
|
|
@ -167,14 +167,14 @@ private:
|
|||
const LibertyLibrary *drvr_library,
|
||||
const Parasitic *parasitic,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew) override;
|
||||
double &wire_delay,
|
||||
double &load_slew) override;
|
||||
void loadDelay(double drvr_slew,
|
||||
Parasitic *pole_residue,
|
||||
double p1,
|
||||
double k1,
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew);
|
||||
double &wire_delay,
|
||||
double &load_slew);
|
||||
float loadDelay(double vth,
|
||||
double p1,
|
||||
double p2,
|
||||
|
|
@ -267,8 +267,8 @@ DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *,
|
|||
{
|
||||
const Parasitics *parasitics = scene->parasitics(min_max);
|
||||
ArcDcalcResult dcalc_result(load_pin_index_map.size());
|
||||
ArcDelay wire_delay = 0.0;
|
||||
Slew load_slew = in_slew;
|
||||
double wire_delay = 0.0;
|
||||
double load_slew = in_slew;
|
||||
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
|
||||
for (const auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
if (parasitics->isPiPoleResidue(parasitic)) {
|
||||
|
|
@ -323,8 +323,8 @@ DmpCeffTwoPoleDelayCalc::loadDelaySlew(const Pin *load_pin,
|
|||
const LibertyLibrary *drvr_library,
|
||||
const Parasitic *parasitic,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew)
|
||||
double &wire_delay,
|
||||
double &load_slew)
|
||||
{
|
||||
parasitic_is_pole_residue_ = parasitics_->isPiPoleResidue(parasitic);
|
||||
// Should handle PiElmore parasitic.
|
||||
|
|
@ -362,12 +362,12 @@ DmpCeffTwoPoleDelayCalc::loadDelay(double drvr_slew,
|
|||
double p1,
|
||||
double k1,
|
||||
// Return values.
|
||||
ArcDelay &wire_delay,
|
||||
Slew &load_slew)
|
||||
double &wire_delay,
|
||||
double &load_slew)
|
||||
{
|
||||
ComplexFloat pole2, residue2;
|
||||
parasitics_->poleResidue(pole_residue, 1, pole2, residue2);
|
||||
if (!delayZero(drvr_slew)
|
||||
if (!delayZero(drvr_slew, this)
|
||||
&& pole2.imag() == 0.0
|
||||
&& residue2.imag() == 0.0) {
|
||||
double p2 = pole2.real();
|
||||
|
|
|
|||
|
|
@ -251,8 +251,8 @@ GraphDelayCalc::delayInvalid(const Pin *pin)
|
|||
void
|
||||
GraphDelayCalc::delayInvalid(Vertex *vertex)
|
||||
{
|
||||
debugPrint(debug_, "delay_calc", 2, "delay invalid %s",
|
||||
vertex->to_string(this).c_str());
|
||||
debugPrint(debug_, "delay_calc", 2, "delay invalid {}",
|
||||
vertex->to_string(this));
|
||||
if (graph_ && incremental_) {
|
||||
invalid_delays_.insert(vertex);
|
||||
// Invalidate driver that triggers dcalc for multi-driver nets.
|
||||
|
|
@ -340,7 +340,7 @@ GraphDelayCalc::findDelays(Level level)
|
|||
if (arc_delay_calc_) {
|
||||
Stats stats(debug_, report_);
|
||||
int dcalc_count = 0;
|
||||
debugPrint(debug_, "delay_calc", 1, "find delays to level %d", level);
|
||||
debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level);
|
||||
if (!delays_seeded_) {
|
||||
iter_->clear();
|
||||
seedRootSlews();
|
||||
|
|
@ -368,7 +368,7 @@ GraphDelayCalc::findDelays(Level level)
|
|||
|
||||
delays_exist_ = true;
|
||||
incremental_ = true;
|
||||
debugPrint(debug_, "delay_calc", 1, "found %d delays", dcalc_count);
|
||||
debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count);
|
||||
stats.report("Delay calc");
|
||||
}
|
||||
}
|
||||
|
|
@ -404,8 +404,8 @@ GraphDelayCalc::seedDrvrSlew(Vertex *drvr_vertex,
|
|||
ArcDelayCalc *arc_delay_calc)
|
||||
{
|
||||
const Pin *drvr_pin = drvr_vertex->pin();
|
||||
debugPrint(debug_, "delay_calc", 2, "seed driver slew %s",
|
||||
drvr_vertex->to_string(this).c_str());
|
||||
debugPrint(debug_, "delay_calc", 2, "seed driver slew {}",
|
||||
drvr_vertex->to_string(this));
|
||||
for (const Scene *scene : scenes_) {
|
||||
const Sdc *sdc = scene->sdc();
|
||||
for (const MinMax *min_max : MinMax::range()) {
|
||||
|
|
@ -504,7 +504,7 @@ GraphDelayCalc::seedNoDrvrSlew(Vertex *drvr_vertex,
|
|||
ArcDelayCalc *arc_delay_calc)
|
||||
{
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
Slew slew(default_slew);
|
||||
Slew slew = default_slew;
|
||||
// Top level bidirect driver uses load slew unless
|
||||
// bidirect instance paths are disabled.
|
||||
if (bidirectDrvrSlewFromLoad(drvr_pin)) {
|
||||
|
|
@ -527,30 +527,30 @@ void
|
|||
GraphDelayCalc::seedLoadSlew(Vertex *vertex)
|
||||
{
|
||||
const Pin *pin = vertex->pin();
|
||||
debugPrint(debug_, "delay_calc", 2, "seed load slew %s",
|
||||
vertex->to_string(this).c_str());
|
||||
debugPrint(debug_, "delay_calc", 2, "seed load slew {}",
|
||||
vertex->to_string(this));
|
||||
initSlew(vertex);
|
||||
for (const Scene *scene : scenes_) {
|
||||
const Sdc *sdc = scene->sdc();
|
||||
for (const MinMax *min_max : MinMax::range()) {
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
for (const RiseFall *rf : RiseFall::range()) {
|
||||
for (const RiseFall *rf : RiseFall::range()) {
|
||||
ClockSet *clks = sdc->findLeafPinClocks(pin);
|
||||
if (!vertex->slewAnnotated(rf, min_max)) {
|
||||
float slew = 0.0;
|
||||
if (clks) {
|
||||
float slew = 0.0;
|
||||
if (clks) {
|
||||
slew = min_max->initValue();
|
||||
for (Clock *clk : *clks) {
|
||||
float clk_slew = clk->slew(rf, min_max);
|
||||
if (min_max->compare(clk_slew, slew))
|
||||
slew = clk_slew;
|
||||
}
|
||||
}
|
||||
graph_->setSlew(vertex, rf, ap_index, slew);
|
||||
slew = clk_slew;
|
||||
}
|
||||
}
|
||||
graph_->setSlew(vertex, rf, ap_index, slew);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a driving cell does not specify a -from_pin, the first port
|
||||
|
|
@ -602,7 +602,7 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell,
|
|||
const Scene *scene,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
debugPrint(debug_, "delay_calc", 2, " driver cell %s %s",
|
||||
debugPrint(debug_, "delay_calc", 2, " driver cell {} {}",
|
||||
drvr_cell->name(),
|
||||
rf->shortName());
|
||||
for (TimingArcSet *arc_set : drvr_cell->timingArcSets(from_port, to_port)) {
|
||||
|
|
@ -627,12 +627,12 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin,
|
|||
const Scene *scene,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
debugPrint(debug_, "delay_calc", 3, " %s %s -> %s %s (%s)",
|
||||
debugPrint(debug_, "delay_calc", 3, " {} {} -> {} {} ({})",
|
||||
arc->from()->name(),
|
||||
arc->fromEdge()->to_string().c_str(),
|
||||
arc->fromEdge()->to_string(),
|
||||
arc->to()->name(),
|
||||
arc->toEdge()->to_string().c_str(),
|
||||
arc->role()->to_string().c_str());
|
||||
arc->toEdge()->to_string(),
|
||||
arc->role()->to_string());
|
||||
const RiseFall *drvr_rf = arc->toEdge()->asRiseFall();
|
||||
if (drvr_rf) {
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
|
|
@ -646,19 +646,19 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin,
|
|||
ArcDcalcResult intrinsic_result =
|
||||
arc_delay_calc_->gateDelay(drvr_pin, arc, Slew(from_slew), 0.0, nullptr,
|
||||
load_pin_index_map, scene, min_max);
|
||||
ArcDelay intrinsic_delay = intrinsic_result.gateDelay();
|
||||
const ArcDelay &intrinsic_delay = intrinsic_result.gateDelay();
|
||||
|
||||
ArcDcalcResult gate_result = arc_delay_calc_->gateDelay(drvr_pin, arc,
|
||||
Slew(from_slew), load_cap,
|
||||
parasitic,
|
||||
load_pin_index_map,
|
||||
scene, min_max);
|
||||
ArcDelay gate_delay = gate_result.gateDelay();
|
||||
Slew gate_slew = gate_result.drvrSlew();
|
||||
const ArcDelay &gate_delay = gate_result.gateDelay();
|
||||
const Slew &gate_slew = gate_result.drvrSlew();
|
||||
|
||||
ArcDelay load_delay = gate_delay - intrinsic_delay;
|
||||
const ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" gate delay = %s intrinsic = %s slew = %s",
|
||||
" gate delay = {} intrinsic = {} slew = {}",
|
||||
delayAsString(gate_delay, this),
|
||||
delayAsString(intrinsic_delay, this),
|
||||
delayAsString(gate_slew, this));
|
||||
|
|
@ -681,8 +681,8 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex,
|
|||
bool propagate)
|
||||
{
|
||||
const Pin *pin = vertex->pin();
|
||||
debugPrint(debug_, "delay_calc", 2, "find delays %s (%s)",
|
||||
vertex->to_string(this).c_str(),
|
||||
debugPrint(debug_, "delay_calc", 2, "find delays {} ({})",
|
||||
vertex->to_string(this),
|
||||
network_->cellName(network_->instance(pin)));
|
||||
if (vertex->isRoot())
|
||||
seedRootSlew(vertex, arc_delay_calc);
|
||||
|
|
@ -729,9 +729,8 @@ GraphDelayCalc::loadSlews(LoadPinIndexMap &load_pin_index_map)
|
|||
Vertex *load_vertex = graph_->pinLoadVertex(pin);
|
||||
SlewSeq &slews = load_slews[index];;
|
||||
slews.resize(slew_count);
|
||||
Slew *vertex_slews = load_vertex->slews();
|
||||
for (size_t i = 0; i < slew_count; i++)
|
||||
slews[i] = vertex_slews[i];
|
||||
slews[i] = graph_->slew(load_vertex, i);
|
||||
}
|
||||
return load_slews;
|
||||
}
|
||||
|
|
@ -744,9 +743,9 @@ GraphDelayCalc::loadSlewsChanged(DrvrLoadSlews &load_slews_prev,
|
|||
for (auto const [pin, index] : load_pin_index_map) {
|
||||
Vertex *load_vertex = graph_->pinLoadVertex(pin);
|
||||
SlewSeq &slews_prev = load_slews_prev[index];;
|
||||
const Slew *slews = load_vertex->slews();
|
||||
for (size_t i = 0; i < slew_count; i++) {
|
||||
if (!delayEqual(slews[i], slews_prev[i]))
|
||||
const Slew slew = graph_->slew(load_vertex, i);
|
||||
if (!delayEqual(slew, slews_prev[i], this))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -886,7 +885,7 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex)
|
|||
Vertex *drvr = edge->from(graph_);
|
||||
const Pin *drvr_pin = drvr->pin();
|
||||
if (isLeafDriver(drvr_pin, network_)) {
|
||||
debugPrint(debug_, "delay_calc", 3, " %s",
|
||||
debugPrint(debug_, "delay_calc", 3, " {}",
|
||||
network_->pathName(drvr_pin));
|
||||
multi_drvr_net_map_[drvr] = multi_drvr;
|
||||
drvr_vertices.push_back(drvr);
|
||||
|
|
@ -915,19 +914,18 @@ GraphDelayCalc::initLoadSlews(Vertex *drvr_vertex)
|
|||
Edge *wire_edge = edge_iter.next();
|
||||
if (wire_edge->isWire()) {
|
||||
Vertex *load_vertex = wire_edge->to(graph_);
|
||||
|
||||
for (Scene *scene : scenes_) {
|
||||
for (const MinMax *min_max : MinMax::range()) {
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
Slew slew_init_value(min_max->initValue());
|
||||
for (const RiseFall *rf : RiseFall::range()) {
|
||||
for (const RiseFall *rf : RiseFall::range()) {
|
||||
if (!load_vertex->slewAnnotated(rf, min_max))
|
||||
graph_->setSlew(load_vertex, rf, ap_index, slew_init_value);
|
||||
graph_->setSlew(load_vertex, rf, ap_index, slew_init_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -965,12 +963,12 @@ GraphDelayCalc::initRootSlews(Vertex *vertex)
|
|||
for (Scene *scene : scenes_) {
|
||||
for (const MinMax *min_max : MinMax::range()) {
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
for (const RiseFall *rf : RiseFall::range()) {
|
||||
for (const RiseFall *rf : RiseFall::range()) {
|
||||
if (!vertex->slewAnnotated(rf, min_max))
|
||||
graph_->setSlew(vertex, rf, ap_index, default_slew);
|
||||
graph_->setSlew(vertex, rf, ap_index, default_slew);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -979,7 +977,7 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge)
|
|||
Vertex *drvr_vertex = edge->to(graph_);
|
||||
const Pin *drvr_pin = drvr_vertex->pin();
|
||||
Instance *drvr_inst = network_->instance(drvr_pin);
|
||||
debugPrint(debug_, "delay_calc", 2, "find latch D->Q %s",
|
||||
debugPrint(debug_, "delay_calc", 2, "find latch D->Q {}",
|
||||
sdc_network_->pathName(drvr_inst));
|
||||
std::array<bool, RiseFall::index_count> delay_exists = {false, false};
|
||||
LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex);
|
||||
|
|
@ -1195,36 +1193,36 @@ GraphDelayCalc::annotateDelaysSlews(Edge *edge,
|
|||
bool
|
||||
GraphDelayCalc::annotateDelaySlew(Edge *edge,
|
||||
const TimingArc *arc,
|
||||
ArcDelay &gate_delay,
|
||||
Slew &gate_slew,
|
||||
const ArcDelay &gate_delay,
|
||||
const Slew &gate_slew,
|
||||
const Scene *scene,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" %s %s -> %s %s (%s) scene:%s/%s",
|
||||
" {} {} -> {} {} ({}) scene:{}/{}",
|
||||
arc->from()->name(),
|
||||
arc->fromEdge()->to_string().c_str(),
|
||||
arc->fromEdge()->to_string(),
|
||||
arc->to()->name(),
|
||||
arc->toEdge()->to_string().c_str(),
|
||||
arc->role()->to_string().c_str(),
|
||||
scene->name().c_str(),
|
||||
min_max->to_string().c_str());
|
||||
arc->toEdge()->to_string(),
|
||||
arc->role()->to_string(),
|
||||
scene->name(),
|
||||
min_max->to_string());
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" gate delay = %s slew = %s",
|
||||
" gate delay = {} slew = {}",
|
||||
delayAsString(gate_delay, this),
|
||||
delayAsString(gate_slew, this));
|
||||
bool delay_changed = false;
|
||||
Vertex *drvr_vertex = edge->to(graph_);
|
||||
const RiseFall *drvr_rf = arc->toEdge()->asRiseFall();
|
||||
// Merge slews.
|
||||
const Slew &drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index);
|
||||
const Slew drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index);
|
||||
if (delayGreater(gate_slew, drvr_slew, min_max, this)
|
||||
&& !drvr_vertex->slewAnnotated(drvr_rf, min_max)
|
||||
&& !edge->role()->isLatchDtoQ())
|
||||
graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew);
|
||||
if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) {
|
||||
const ArcDelay &prev_gate_delay = graph_->arcDelay(edge,arc,ap_index);
|
||||
const ArcDelay prev_gate_delay = graph_->arcDelay(edge,arc,ap_index);
|
||||
float gate_delay1 = delayAsFloat(gate_delay);
|
||||
float prev_gate_delay1 = delayAsFloat(prev_gate_delay);
|
||||
if (prev_gate_delay1 == 0.0
|
||||
|
|
@ -1258,23 +1256,23 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex,
|
|||
Vertex *load_vertex = wire_edge->to(graph_);
|
||||
Pin *load_pin = load_vertex->pin();
|
||||
size_t load_idx = load_pin_index_map[load_pin];
|
||||
ArcDelay wire_delay = dcalc_result.wireDelay(load_idx);
|
||||
Slew load_slew = dcalc_result.loadSlew(load_idx);
|
||||
const ArcDelay &wire_delay = dcalc_result.wireDelay(load_idx);
|
||||
const Slew &load_slew = dcalc_result.loadSlew(load_idx);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" %s load delay = %s slew = %s",
|
||||
load_vertex->to_string(this).c_str(),
|
||||
" {} load delay = {} slew = {}",
|
||||
load_vertex->to_string(this),
|
||||
delayAsString(wire_delay, this),
|
||||
delayAsString(load_slew, this));
|
||||
bool load_changed = false;
|
||||
if (!load_vertex->slewAnnotated(drvr_rf, min_max)) {
|
||||
if (drvr_vertex->slewAnnotated(drvr_rf, min_max)) {
|
||||
// Copy the driver slew to the load if it is annotated.
|
||||
const Slew &drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index);
|
||||
const Slew drvr_slew = graph_->slew(drvr_vertex,drvr_rf,ap_index);
|
||||
graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew);
|
||||
load_changed = true;
|
||||
}
|
||||
else {
|
||||
const Slew &slew = graph_->slew(load_vertex, drvr_rf, ap_index);
|
||||
const Slew slew = graph_->slew(load_vertex, drvr_rf, ap_index);
|
||||
if (!merge
|
||||
|| delayGreater(load_slew, slew, min_max, this)) {
|
||||
graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew);
|
||||
|
|
@ -1287,7 +1285,7 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex,
|
|||
// annotate the same wire edges so they must be combined
|
||||
// rather than set.
|
||||
const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, ap_index);
|
||||
Delay wire_delay_extra = extra_delay + wire_delay;
|
||||
Delay wire_delay_extra = delaySum(extra_delay, wire_delay, this);
|
||||
if (!merge
|
||||
|| delayGreater(wire_delay_extra, delay, min_max, this)) {
|
||||
graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay_extra);
|
||||
|
|
@ -1582,7 +1580,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
|
|||
TimingArcSet *arc_set = edge->timingArcSet();
|
||||
const Pin *to_pin = to_vertex->pin();
|
||||
Instance *inst = network_->instance(to_pin);
|
||||
debugPrint(debug_, "delay_calc", 2, "find check %s %s -> %s",
|
||||
debugPrint(debug_, "delay_calc", 2, "find check {} {} -> {}",
|
||||
sdc_network_->pathName(inst),
|
||||
network_->portName(from_vertex->pin()),
|
||||
network_->portName(to_pin));
|
||||
|
|
@ -1602,18 +1600,18 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
|
|||
if (!graph_->arcDelayAnnotated(edge, arc, ap_index)) {
|
||||
const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf,
|
||||
scene, min_max);
|
||||
const Slew &to_slew = graph_->slew(to_vertex, to_rf, ap_index);
|
||||
const Slew to_slew = graph_->slew(to_vertex, to_rf, ap_index);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" %s %s -> %s %s (%s) scene:%s/%s",
|
||||
" {} {} -> {} {} ({}) scene:{}/{}",
|
||||
arc_set->from()->name(),
|
||||
arc->fromEdge()->to_string().c_str(),
|
||||
arc->fromEdge()->to_string(),
|
||||
arc_set->to()->name(),
|
||||
arc->toEdge()->to_string().c_str(),
|
||||
arc_set->role()->to_string().c_str(),
|
||||
scene->name().c_str(),
|
||||
min_max->to_string().c_str());
|
||||
arc->toEdge()->to_string(),
|
||||
arc_set->role()->to_string(),
|
||||
scene->name(),
|
||||
min_max->to_string());
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" from_slew = %s to_slew = %s",
|
||||
" from_slew = {} to_slew = {}",
|
||||
delayAsString(from_slew, this),
|
||||
delayAsString(to_slew, this));
|
||||
float related_out_cap = 0.0;
|
||||
|
|
@ -1624,7 +1622,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
|
|||
to_slew, related_out_cap,
|
||||
scene, min_max);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" check_delay = %s",
|
||||
" check_delay = {}",
|
||||
delayAsString(check_delay, this));
|
||||
graph_->setArcDelay(edge, arc, ap_index, check_delay);
|
||||
delay_changed = true;
|
||||
|
|
@ -1684,7 +1682,7 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge,
|
|||
if (role->isTimingCheck()) {
|
||||
const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, scene, min_max);
|
||||
DcalcAPIndex slew_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
const Slew &to_slew = graph_->slew(to_vertex, to_rf, slew_index);
|
||||
const Slew to_slew = graph_->slew(to_vertex, to_rf, slew_index);
|
||||
const ClkNetwork *clk_network = scene->mode()->clkNetwork();
|
||||
bool from_ideal_clk = clk_network->isIdealClock(from_vertex);
|
||||
const char *from_slew_annotation = from_ideal_clk ? " (ideal clock)" : nullptr;
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ LumpedCapDelayCalc::inputPortDelay(const Pin *,
|
|||
const MinMax *)
|
||||
{
|
||||
const LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
|
||||
return makeResult(drvr_library,rf, 0.0, in_slew, load_pin_index_map);
|
||||
return makeResult(drvr_library,rf, delay_zero, in_slew, load_pin_index_map);
|
||||
}
|
||||
|
||||
ArcDcalcResult
|
||||
|
|
@ -133,22 +133,28 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
{
|
||||
GateTimingModel *model = arc->gateModel(scene, min_max);
|
||||
debugPrint(debug_, "delay_calc", 3,
|
||||
" in_slew = %s load_cap = %s lumped",
|
||||
" in_slew = {} load_cap = {} lumped",
|
||||
delayAsString(in_slew, this),
|
||||
units()->capacitanceUnit()->asString(load_cap));
|
||||
const RiseFall *rf = arc->toEdge()->asRiseFall();
|
||||
const LibertyLibrary *drvr_library = arc->to()->libertyLibrary();
|
||||
if (model) {
|
||||
ArcDelay gate_delay;
|
||||
Slew drvr_slew;
|
||||
float gate_delay, drvr_slew;
|
||||
float in_slew1 = delayAsFloat(in_slew);
|
||||
// NaNs cause seg faults during table lookup.
|
||||
if (std::isnan(load_cap) || std::isnan(delayAsFloat(in_slew)))
|
||||
if (std::isnan(load_cap) || std::isnan(in_slew.mean()))
|
||||
report_->error(1350, "gate delay input variable is NaN");
|
||||
model->gateDelay(pinPvt(drvr_pin, scene, min_max), in_slew1, load_cap,
|
||||
variables_->pocvEnabled(),
|
||||
gate_delay, drvr_slew);
|
||||
return makeResult(drvr_library, rf, gate_delay, drvr_slew, load_pin_index_map);
|
||||
const Pvt *pvt = pinPvt(drvr_pin, scene, min_max);
|
||||
model->gateDelay(pvt, in_slew1, load_cap, gate_delay, drvr_slew);
|
||||
|
||||
// Fill in pocv parameters.
|
||||
ArcDelay gate_delay2(gate_delay);
|
||||
Slew drvr_slew2(drvr_slew);
|
||||
if (variables_->pocvEnabled())
|
||||
model->gateDelayPocv(pvt, in_slew1, load_cap, min_max, variables_->pocvMode(),
|
||||
gate_delay2, drvr_slew2);
|
||||
|
||||
return makeResult(drvr_library, rf, gate_delay2, drvr_slew2, load_pin_index_map);
|
||||
}
|
||||
else
|
||||
return makeResult(drvr_library, rf, delay_zero, delay_zero, load_pin_index_map);
|
||||
|
|
@ -157,17 +163,18 @@ LumpedCapDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
ArcDcalcResult
|
||||
LumpedCapDelayCalc::makeResult(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
ArcDelay gate_delay,
|
||||
Slew drvr_slew,
|
||||
const ArcDelay &gate_delay,
|
||||
const Slew &drvr_slew,
|
||||
const LoadPinIndexMap &load_pin_index_map)
|
||||
{
|
||||
ArcDcalcResult dcalc_result(load_pin_index_map.size());
|
||||
dcalc_result.setGateDelay(gate_delay);
|
||||
dcalc_result.setDrvrSlew(drvr_slew);
|
||||
|
||||
double drvr_slew1 = delayAsFloat(drvr_slew);
|
||||
for (const auto [load_pin, load_idx] : load_pin_index_map) {
|
||||
ArcDelay wire_delay = 0.0;
|
||||
Slew load_slew = drvr_slew;
|
||||
double wire_delay = 0.0;
|
||||
double load_slew = drvr_slew1;
|
||||
thresholdAdjust(load_pin, drvr_library, rf, wire_delay, load_slew);
|
||||
dcalc_result.setWireDelay(load_idx, wire_delay);
|
||||
dcalc_result.setLoadSlew(load_idx, load_slew);
|
||||
|
|
@ -190,7 +197,8 @@ LumpedCapDelayCalc::reportGateDelay(const Pin *check_pin,
|
|||
if (model) {
|
||||
float in_slew1 = delayAsFloat(in_slew);
|
||||
return model->reportGateDelay(pinPvt(check_pin, scene, min_max),
|
||||
in_slew1, load_cap, false, digits);
|
||||
in_slew1, load_cap, min_max,
|
||||
PocvMode::scalar, digits);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ public:
|
|||
protected:
|
||||
ArcDcalcResult makeResult(const LibertyLibrary *drvr_library,
|
||||
const RiseFall *rf,
|
||||
ArcDelay gate_delay,
|
||||
Slew drvr_slew,
|
||||
const ArcDelay &gate_delay,
|
||||
const Slew &drvr_slew,
|
||||
const LoadPinIndexMap &load_pin_index_map);
|
||||
|
||||
using ArcDelayCalc::reduceParasitic;
|
||||
|
|
|
|||
|
|
@ -88,13 +88,13 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
|
|||
load_pin_index_map, scene, min_max);
|
||||
ArcDelay gate_delay = gate_result.gateDelay();
|
||||
Slew drvr_slew = gate_result.drvrSlew();
|
||||
ArcDelay load_delay = gate_delay - intrinsic_delay;
|
||||
ArcDelay load_delay = delayDiff(gate_delay, intrinsic_delay, this);
|
||||
load_delays[drvr_idx] = load_delay;
|
||||
|
||||
if (!delayZero(load_delay))
|
||||
load_delay_sum += 1.0 / load_delay;
|
||||
if (!delayZero(drvr_slew))
|
||||
slew_sum += 1.0 / drvr_slew;
|
||||
if (!delayZero(load_delay, this))
|
||||
delayIncr(load_delay_sum, delayDiv(1.0, load_delay, this), this);
|
||||
if (!delayZero(drvr_slew, this))
|
||||
delayIncr(slew_sum, delayDiv(1.0, drvr_slew, this), this);
|
||||
|
||||
dcalc_result.setLoadCount(load_pin_index_map.size());
|
||||
for (const auto &[load_pin, load_idx] : load_pin_index_map) {
|
||||
|
|
@ -103,14 +103,16 @@ ParallelDelayCalc::gateDelaysParallel(ArcDcalcArgSeq &dcalc_args,
|
|||
}
|
||||
}
|
||||
|
||||
ArcDelay gate_load_delay = delayZero(load_delay_sum)
|
||||
ArcDelay gate_load_delay = delayZero(load_delay_sum, this)
|
||||
? delay_zero
|
||||
: 1.0 / load_delay_sum;
|
||||
ArcDelay drvr_slew = delayZero(slew_sum) ? delay_zero : 1.0 / slew_sum;
|
||||
: delayDiv(1.0, load_delay_sum, this);
|
||||
ArcDelay drvr_slew = delayZero(slew_sum, this)
|
||||
? delay_zero
|
||||
: delayDiv(1.0, slew_sum, this);
|
||||
|
||||
for (size_t drvr_idx = 0; drvr_idx < drvr_count; drvr_idx++) {
|
||||
ArcDcalcResult &dcalc_result = dcalc_results[drvr_idx];
|
||||
dcalc_result.setGateDelay(intrinsic_delays[drvr_idx] + gate_load_delay);
|
||||
dcalc_result.setGateDelay(delaySum(intrinsic_delays[drvr_idx], gate_load_delay, this));
|
||||
dcalc_result.setDrvrSlew(drvr_slew);
|
||||
}
|
||||
return dcalc_results;
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "PrimaDelayCalc.hh"
|
||||
|
||||
#include <cmath> // abs
|
||||
#include <cmath> // abs
|
||||
|
||||
#include "Debug.hh"
|
||||
#include "Units.hh"
|
||||
|
|
@ -38,15 +38,16 @@
|
|||
#include "Parasitics.hh"
|
||||
#include "GraphDelayCalc.hh"
|
||||
#include "DmpDelayCalc.hh"
|
||||
#include "Format.hh"
|
||||
|
||||
#include <Eigen/LU>
|
||||
#include <Eigen/QR>
|
||||
|
||||
namespace sta {
|
||||
|
||||
using Eigen::SparseLU;
|
||||
using Eigen::HouseholderQR;
|
||||
using Eigen::ColPivHouseholderQR;
|
||||
using Eigen::HouseholderQR;
|
||||
using Eigen::SparseLU;
|
||||
|
||||
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
|
||||
// McGraw-Hill, Inc. New York, NY.
|
||||
|
|
@ -90,10 +91,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) :
|
|||
{
|
||||
}
|
||||
|
||||
PrimaDelayCalc::~PrimaDelayCalc()
|
||||
{
|
||||
delete table_dcalc_;
|
||||
}
|
||||
PrimaDelayCalc::~PrimaDelayCalc() { delete table_dcalc_; }
|
||||
|
||||
ArcDelayCalc *
|
||||
PrimaDelayCalc::copy()
|
||||
|
|
@ -130,8 +128,8 @@ PrimaDelayCalc::findParasitic(const Pin *drvr_pin,
|
|||
bool has_wire_cap;
|
||||
graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap,
|
||||
fanout, has_wire_cap);
|
||||
parasitic = parasitics->makeWireloadNetwork(drvr_pin, wireload,
|
||||
fanout, scene, min_max);
|
||||
parasitic =
|
||||
parasitics->makeWireloadNetwork(drvr_pin, wireload, fanout, scene, min_max);
|
||||
}
|
||||
return parasitic;
|
||||
}
|
||||
|
|
@ -160,14 +158,14 @@ PrimaDelayCalc::inputPortDelay(const Pin *drvr_pin,
|
|||
LibertyLibrary *drvr_library = network_->defaultLibertyLibrary();
|
||||
const Parasitic *pi_elmore = nullptr;
|
||||
if (parasitic && parasitics->isParasiticNetwork(parasitic))
|
||||
pi_elmore = parasitics->reduceToPiElmore(parasitic, drvr_pin, rf,
|
||||
scene, min_max);
|
||||
pi_elmore =
|
||||
parasitics->reduceToPiElmore(parasitic, drvr_pin, rf, scene, min_max);
|
||||
|
||||
for (auto load_pin_index : load_pin_index_map) {
|
||||
const Pin *load_pin = load_pin_index.first;
|
||||
size_t load_idx = load_pin_index.second;
|
||||
ArcDelay wire_delay = 0.0;
|
||||
Slew load_slew = in_slew;
|
||||
double wire_delay = 0.0;
|
||||
double load_slew = in_slew;
|
||||
bool elmore_exists = false;
|
||||
float elmore = 0.0;
|
||||
if (pi_elmore)
|
||||
|
|
@ -190,12 +188,13 @@ PrimaDelayCalc::gateDelay(const Pin *drvr_pin,
|
|||
const Parasitic *parasitic,
|
||||
const LoadPinIndexMap &load_pin_index_map,
|
||||
const Scene *scene,
|
||||
const MinMax *min_max)
|
||||
const MinMax *min_max)
|
||||
{
|
||||
ArcDcalcArgSeq dcalc_args;
|
||||
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap, parasitic);
|
||||
ArcDcalcResultSeq dcalc_results = gateDelays(dcalc_args, load_pin_index_map,
|
||||
scene, min_max);
|
||||
dcalc_args.emplace_back(nullptr, drvr_pin, nullptr, arc, in_slew, load_cap,
|
||||
parasitic);
|
||||
ArcDcalcResultSeq dcalc_results =
|
||||
gateDelays(dcalc_args, load_pin_index_map, scene, min_max);
|
||||
return dcalc_results[0];
|
||||
}
|
||||
|
||||
|
|
@ -229,15 +228,15 @@ PrimaDelayCalc::gateDelays(ArcDcalcArgSeq &dcalc_args,
|
|||
&& output_waveforms->slewAxis()->inBounds(in_slew)
|
||||
&& output_waveforms->capAxis()->inBounds(dcalc_arg.loadCap())) {
|
||||
output_waveforms_[drvr_idx] = output_waveforms;
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "%s %s",
|
||||
dcalc_arg.drvrCell()->name(),
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "{} {}", dcalc_arg.drvrCell()->name(),
|
||||
drvr_rf_->shortName());
|
||||
LibertyCell *drvr_cell = dcalc_arg.drvrCell();
|
||||
const LibertyLibrary *drvr_library = drvr_cell->libertyLibrary();
|
||||
bool vdd_exists;
|
||||
drvr_library->supplyVoltage("VDD", vdd_, vdd_exists);
|
||||
if (!vdd_exists)
|
||||
report_->error(1720, "VDD not defined in library %s", drvr_library->name());
|
||||
report_->error(1720, "VDD not defined in library {}",
|
||||
drvr_library->name());
|
||||
drvr_cell->ensureVoltageWaveforms(scenes_);
|
||||
if (drvr_idx == 0) {
|
||||
vth_ = drvr_library->outputThreshold(drvr_rf_) * vdd_;
|
||||
|
|
@ -268,13 +267,13 @@ PrimaDelayCalc::tableDcalcResults()
|
|||
const Pin *drvr_pin = dcalc_arg.drvrPin();
|
||||
if (drvr_pin) {
|
||||
const RiseFall *rf = dcalc_arg.drvrEdge();
|
||||
const Parasitic *parasitic = table_dcalc_->findParasitic(drvr_pin, rf,
|
||||
scene_, min_max_);
|
||||
const Parasitic *parasitic =
|
||||
table_dcalc_->findParasitic(drvr_pin, rf, scene_, min_max_);
|
||||
dcalc_arg.setParasitic(parasitic);
|
||||
}
|
||||
}
|
||||
return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_,
|
||||
scene_, min_max_);
|
||||
return table_dcalc_->gateDelays(*dcalc_args_, *load_pin_index_map_, scene_,
|
||||
min_max_);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -284,8 +283,7 @@ PrimaDelayCalc::simulate()
|
|||
stampEqns();
|
||||
setXinit();
|
||||
|
||||
if (prima_order_ > 0
|
||||
&& node_count_ > prima_order_) {
|
||||
if (prima_order_ > 0 && node_count_ > prima_order_) {
|
||||
primaReduce();
|
||||
simulate1(Gq_, Cq_, Bq_, xq_init_, Vq_, prima_order_);
|
||||
}
|
||||
|
|
@ -297,11 +295,11 @@ PrimaDelayCalc::simulate()
|
|||
|
||||
void
|
||||
PrimaDelayCalc::simulate1(const MatrixSd &G,
|
||||
const MatrixSd &C,
|
||||
const Eigen::MatrixXd &B,
|
||||
const Eigen::VectorXd &x_init,
|
||||
const Eigen::MatrixXd &x_to_v,
|
||||
const size_t order)
|
||||
const MatrixSd &C,
|
||||
const Eigen::MatrixXd &B,
|
||||
const Eigen::VectorXd &x_init,
|
||||
const Eigen::MatrixXd &x_to_v,
|
||||
const size_t order)
|
||||
{
|
||||
Eigen::VectorXd x(order);
|
||||
Eigen::VectorXd x_prev(order);
|
||||
|
|
@ -315,7 +313,8 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
|
|||
v_ = v_prev_ = x_to_v * x_init;
|
||||
|
||||
time_step_ = time_step_prev_ = timeStep();
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "time step %s", delayAsString(time_step_, this));
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "time step {}",
|
||||
delayAsString(time_step_, this));
|
||||
|
||||
MatrixSd A(order, order);
|
||||
A = G + (2.0 / time_step_) * C;
|
||||
|
|
@ -336,8 +335,8 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
|
|||
v_ = v_prev_ = x_to_v * x_init;
|
||||
|
||||
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
|
||||
double time_begin = output_waveforms_[0]->voltageTime((*dcalc_args_)[0].inSlewFlt(),
|
||||
ceff_[0], 0.0);
|
||||
double time_begin = output_waveforms_[0]->voltageTime(
|
||||
(*dcalc_args_)[0].inSlewFlt(), ceff_[0], 0.0);
|
||||
// Limit in case load voltage waveforms don't get to final value.
|
||||
double time_end = time_begin + maxTime();
|
||||
|
||||
|
|
@ -349,9 +348,9 @@ PrimaDelayCalc::simulate1(const MatrixSd &G,
|
|||
rhs = B * u_ + (1.0 / time_step_) * C * (3.0 * x_prev - x_prev2);
|
||||
x = A_solver.solve(rhs);
|
||||
v_ = x_to_v * x;
|
||||
|
||||
|
||||
const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[0];
|
||||
debugPrint(debug_, "ccs_dcalc", 3, "%s ceff %s VDrvr %.4f Idrvr %s",
|
||||
debugPrint(debug_, "ccs_dcalc", 3, "{} ceff {} VDrvr {:.4f} Idrvr {}",
|
||||
delayAsString(time, this),
|
||||
units_->capacitanceUnit()->asString(ceff_[0]),
|
||||
voltage(dcalc_arg.drvrPin()),
|
||||
|
|
@ -384,7 +383,7 @@ double
|
|||
PrimaDelayCalc::maxTime()
|
||||
{
|
||||
return (*dcalc_args_)[0].inSlewFlt()
|
||||
+ (driverResistance() + resistance_sum_) * load_cap_ * 4;
|
||||
+ (driverResistance() + resistance_sum_) * load_cap_ * 4;
|
||||
}
|
||||
|
||||
float
|
||||
|
|
@ -429,9 +428,8 @@ PrimaDelayCalc::findNodeCount()
|
|||
const Pin *pin = parasitics_->pin(node);
|
||||
if (pin) {
|
||||
pin_node_map_[pin] = node_idx;
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "pin %s node %lu",
|
||||
network_->pathName(pin),
|
||||
node_idx);
|
||||
debugPrint(debug_, "ccs_dcalc", 1, "pin {} node {}",
|
||||
network_->pathName(pin), node_idx);
|
||||
}
|
||||
double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node);
|
||||
node_capacitances_.push_back(cap);
|
||||
|
|
@ -441,14 +439,12 @@ PrimaDelayCalc::findNodeCount()
|
|||
for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) {
|
||||
float cap = parasitics_->value(capacitor) * coupling_cap_multiplier_;
|
||||
ParasiticNode *node1 = parasitics_->node1(capacitor);
|
||||
if (node1
|
||||
&& !parasitics_->isExternal(node1)) {
|
||||
if (node1 && !parasitics_->isExternal(node1)) {
|
||||
size_t node_idx = node_index_map_[node1];
|
||||
node_capacitances_[node_idx] += cap;
|
||||
}
|
||||
ParasiticNode *node2 = parasitics_->node2(capacitor);
|
||||
if (node2
|
||||
&& !parasitics_->isExternal(node2)) {
|
||||
if (node2 && !parasitics_->isExternal(node2)) {
|
||||
size_t node_idx = node_index_map_[node2];
|
||||
node_capacitances_[node_idx] += cap;
|
||||
}
|
||||
|
|
@ -496,9 +492,8 @@ PrimaDelayCalc::initCeffIdrvr()
|
|||
const ArcDcalcArg &dcalc_arg = (*dcalc_args_)[drvr_idx];
|
||||
ceff_[drvr_idx] = load_cap_;
|
||||
// voltageTime is always for a rising waveform so 0.0v is initial voltage.
|
||||
drvr_current_[drvr_idx] =
|
||||
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(),
|
||||
ceff_[drvr_idx], 0.0);
|
||||
drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
|
||||
dcalc_arg.inSlewFlt(), ceff_[drvr_idx], 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -617,8 +612,7 @@ PrimaDelayCalc::updateCeffIdrvr()
|
|||
double v2 = voltagePrev(node_idx);
|
||||
double dv = v1 - v2;
|
||||
if (drvr_rf_ == RiseFall::rise()) {
|
||||
if (drvr_current != 0.0
|
||||
&& dv > 0.0) {
|
||||
if (drvr_current != 0.0 && dv > 0.0) {
|
||||
double ceff = drvr_current * time_step_ / dv;
|
||||
if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff))
|
||||
ceff_[drvr_idx] = ceff;
|
||||
|
|
@ -627,13 +621,11 @@ PrimaDelayCalc::updateCeffIdrvr()
|
|||
// Whoa partner. Head'n for the weeds.
|
||||
drvr_current_[drvr_idx] = 0.0;
|
||||
else
|
||||
drvr_current_[drvr_idx] =
|
||||
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(),
|
||||
ceff_[drvr_idx], v1);
|
||||
drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
|
||||
dcalc_arg.inSlewFlt(), ceff_[drvr_idx], v1);
|
||||
}
|
||||
else {
|
||||
if (drvr_current != 0.0
|
||||
&& dv < 0.0) {
|
||||
if (drvr_current != 0.0 && dv < 0.0) {
|
||||
double ceff = drvr_current * time_step_ / dv;
|
||||
if (output_waveforms_[drvr_idx]->capAxis()->inBounds(ceff))
|
||||
ceff_[drvr_idx] = ceff;
|
||||
|
|
@ -643,10 +635,8 @@ PrimaDelayCalc::updateCeffIdrvr()
|
|||
drvr_current_[drvr_idx] = 0.0;
|
||||
}
|
||||
else
|
||||
drvr_current_[drvr_idx] =
|
||||
output_waveforms_[drvr_idx]->voltageCurrent(dcalc_arg.inSlewFlt(),
|
||||
ceff_[drvr_idx],
|
||||
vdd_ - v1);
|
||||
drvr_current_[drvr_idx] = output_waveforms_[drvr_idx]->voltageCurrent(
|
||||
dcalc_arg.inSlewFlt(), ceff_[drvr_idx], vdd_ - v1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -657,10 +647,8 @@ PrimaDelayCalc::loadWaveformsFinished()
|
|||
for (auto pin_node : pin_node_map_) {
|
||||
size_t node_idx = pin_node.second;
|
||||
double v = voltage(node_idx);
|
||||
if ((drvr_rf_ == RiseFall::rise()
|
||||
&& v < vh_ + (vdd_ - vh_) * .5)
|
||||
|| (drvr_rf_ == RiseFall::fall()
|
||||
&& (v > vl_ * .5))) {
|
||||
if ((drvr_rf_ == RiseFall::rise() && v < vh_ + (vdd_ - vh_) * .5)
|
||||
|| (drvr_rf_ == RiseFall::fall() && (v > vl_ * .5))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -678,12 +666,10 @@ PrimaDelayCalc::measureThresholds(double time)
|
|||
double v_prev = voltagePrev(node_idx);
|
||||
for (size_t m = 0; m < measure_threshold_count_; m++) {
|
||||
double th = measure_thresholds_[m];
|
||||
if ((v_prev < th && th <= v)
|
||||
|| (v_prev > th && th >= v)) {
|
||||
double t_cross = time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
|
||||
debugPrint(debug_, "ccs_measure", 1, "node %lu cross %.2f %s",
|
||||
node_idx,
|
||||
th,
|
||||
if ((v_prev < th && th <= v) || (v_prev > th && th >= v)) {
|
||||
double t_cross =
|
||||
time - time_step_ + (th - v_prev) * time_step_ / (v - v_prev);
|
||||
debugPrint(debug_, "ccs_measure", 1, "node {} cross {:.2f} {}", node_idx, th,
|
||||
delayAsString(t_cross, this));
|
||||
threshold_times_[node_idx][m] = t_cross;
|
||||
}
|
||||
|
|
@ -722,14 +708,12 @@ PrimaDelayCalc::dcalcResults()
|
|||
size_t drvr_node = pin_node_map_[drvr_pin];
|
||||
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
|
||||
float ref_time = output_waveforms_[drvr_idx]->referenceTime(dcalc_arg.inSlewFlt());
|
||||
ArcDelay gate_delay = drvr_times[threshold_vth] - ref_time;
|
||||
Slew drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]);
|
||||
double gate_delay = drvr_times[threshold_vth] - ref_time;
|
||||
double drvr_slew = std::abs(drvr_times[threshold_vh] - drvr_times[threshold_vl]);
|
||||
dcalc_result.setGateDelay(gate_delay);
|
||||
dcalc_result.setDrvrSlew(drvr_slew);
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"%s gate delay %s slew %s",
|
||||
network_->pathName(drvr_pin),
|
||||
delayAsString(gate_delay, this),
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "{} gate delay {} slew {}",
|
||||
network_->pathName(drvr_pin), delayAsString(gate_delay, this),
|
||||
delayAsString(drvr_slew, this));
|
||||
|
||||
dcalc_result.setLoadCount(load_pin_index_map_->size());
|
||||
|
|
@ -739,10 +723,9 @@ PrimaDelayCalc::dcalcResults()
|
|||
size_t load_node = pin_node_map_[load_pin];
|
||||
ThresholdTimes &wire_times = threshold_times_[load_node];
|
||||
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
|
||||
ArcDelay wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
|
||||
Slew load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
|
||||
debugPrint(debug_, "ccs_dcalc", 2,
|
||||
"load %s %s delay %s slew %s",
|
||||
double wire_delay = wire_times[threshold_vth] - drvr_times[threshold_vth];
|
||||
double load_slew = std::abs(wire_times[threshold_vh] - wire_times[threshold_vl]);
|
||||
debugPrint(debug_, "ccs_dcalc", 2, "load {} {} delay {} slew {}",
|
||||
network_->pathName(load_pin),
|
||||
drvr_rf_->shortName(),
|
||||
delayAsString(wire_delay, this),
|
||||
|
|
@ -849,16 +832,18 @@ PrimaDelayCalc::primaReduce2()
|
|||
|
||||
// Modified Gram-Schmidt orthonormalization
|
||||
for (size_t j = 0; j < k; j++) {
|
||||
Eigen::MatrixXd H = Vq.block(0, j * port_count_, order_, port_count_).transpose()
|
||||
* Vq.block(0, k * port_count_, order_, port_count_);
|
||||
Eigen::MatrixXd H =
|
||||
Vq.block(0, j * port_count_, order_, port_count_).transpose()
|
||||
* Vq.block(0, k * port_count_, order_, port_count_);
|
||||
Vq.block(0, k * port_count_, order_, port_count_) =
|
||||
Vq.block(0, k * port_count_, order_, port_count_) - Vq.block(0, j * port_count_, order_, port_count_) * H;
|
||||
Vq.block(0, k * port_count_, order_, port_count_)
|
||||
- Vq.block(0, j * port_count_, order_, port_count_) * H;
|
||||
}
|
||||
Eigen::MatrixXd Vq_k = Vq.block(0, k * port_count_, order_, port_count_);
|
||||
Eigen::HouseholderQR<Eigen::MatrixXd> Vq_k_solver(Vq_k);
|
||||
Eigen::MatrixXd VqQ = Vq_k_solver.householderQ();
|
||||
Vq.block(0, k * port_count_, order_, port_count_) =
|
||||
VqQ.block(0, 0, order_, port_count_);
|
||||
Vq.block(0, k * port_count_, order_, port_count_) =
|
||||
VqQ.block(0, 0, order_, port_count_);
|
||||
}
|
||||
Vq_.resize(order_, prima_order_);
|
||||
Vq_ = Vq.block(0, 0, order_, prima_order_);
|
||||
|
|
@ -920,7 +905,8 @@ PrimaDelayCalc::reportGateDelay(const Pin *drvr_pin,
|
|||
if (model) {
|
||||
float in_slew1 = delayAsFloat(in_slew);
|
||||
return model->reportGateDelay(pinPvt(drvr_pin, scene, min_max),
|
||||
in_slew1, load_cap, false, digits);
|
||||
in_slew1, load_cap, min_max,
|
||||
PocvMode::scalar, digits);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
@ -956,8 +942,8 @@ Waveform
|
|||
PrimaDelayCalc::watchWaveform(const Pin *pin)
|
||||
{
|
||||
FloatSeq &voltages = watch_pin_values_[pin];
|
||||
TableAxisPtr time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
|
||||
FloatSeq(times_));
|
||||
TableAxisPtr time_axis =
|
||||
std::make_shared<TableAxis>(TableAxisVariable::time, FloatSeq(times_));
|
||||
Table waveform(new FloatSeq(voltages), time_axis);
|
||||
return waveform;
|
||||
}
|
||||
|
|
@ -968,7 +954,7 @@ void
|
|||
PrimaDelayCalc::reportMatrix(const char *name,
|
||||
MatrixSd &matrix)
|
||||
{
|
||||
report_->reportLine("%s", name);
|
||||
report_->report("{}", name);
|
||||
reportMatrix(matrix);
|
||||
}
|
||||
|
||||
|
|
@ -976,7 +962,7 @@ void
|
|||
PrimaDelayCalc::reportMatrix(const char *name,
|
||||
Eigen::MatrixXd &matrix)
|
||||
{
|
||||
report_->reportLine("%s", name);
|
||||
report_->report("{}", name);
|
||||
reportMatrix(matrix);
|
||||
}
|
||||
|
||||
|
|
@ -984,7 +970,7 @@ void
|
|||
PrimaDelayCalc::reportMatrix(const char *name,
|
||||
Eigen::VectorXd &matrix)
|
||||
{
|
||||
report_->reportLine("%s", name);
|
||||
report_->report("{}", name);
|
||||
reportMatrix(matrix);
|
||||
}
|
||||
|
||||
|
|
@ -992,22 +978,19 @@ void
|
|||
PrimaDelayCalc::reportVector(const char *name,
|
||||
std::vector<double> &matrix)
|
||||
{
|
||||
report_->reportLine("%s", name);
|
||||
report_->report("{}", name);
|
||||
reportVector(matrix);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PrimaDelayCalc::reportMatrix(MatrixSd &matrix)
|
||||
{
|
||||
for (Eigen::Index i = 0; i < matrix.rows(); i++) {
|
||||
std::string line = "| ";
|
||||
for (Eigen::Index j = 0; j < matrix.cols(); j++) {
|
||||
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
|
||||
line += entry;
|
||||
line += " ";
|
||||
}
|
||||
for (Eigen::Index j = 0; j < matrix.cols(); j++)
|
||||
line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " ";
|
||||
line += "|";
|
||||
report_->reportLineString(line);
|
||||
report_->reportLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1016,13 +999,10 @@ PrimaDelayCalc::reportMatrix(Eigen::MatrixXd &matrix)
|
|||
{
|
||||
for (Eigen::Index i = 0; i < matrix.rows(); i++) {
|
||||
std::string line = "| ";
|
||||
for (Eigen::Index j = 0; j < matrix.cols(); j++) {
|
||||
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i, j));
|
||||
line += entry;
|
||||
line += " ";
|
||||
}
|
||||
for (Eigen::Index j = 0; j < matrix.cols(); j++)
|
||||
line += sta::format("{:10.3e}", matrix.coeff(i, j)) + " ";
|
||||
line += "|";
|
||||
report_->reportLineString(line);
|
||||
report_->reportLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1031,25 +1011,21 @@ PrimaDelayCalc::reportMatrix(Eigen::VectorXd &matrix)
|
|||
{
|
||||
std::string line = "| ";
|
||||
for (Eigen::Index i = 0; i < matrix.rows(); i++) {
|
||||
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i));
|
||||
line += entry;
|
||||
line += " ";
|
||||
std::string entry =
|
||||
line += sta::format("{:10.3e}", matrix.coeff(i)) + " ";
|
||||
}
|
||||
line += "|";
|
||||
report_->reportLineString(line);
|
||||
report_->reportLine(line);
|
||||
}
|
||||
|
||||
void
|
||||
PrimaDelayCalc::reportVector(std::vector<double> &matrix)
|
||||
{
|
||||
std::string line = "| ";
|
||||
for (size_t i = 0; i < matrix.size(); i++) {
|
||||
std::string entry = stdstrPrint("%10.3e", matrix[i]);
|
||||
line += entry;
|
||||
line += " ";
|
||||
}
|
||||
for (size_t i = 0; i < matrix.size(); i++)
|
||||
line += sta::format("{:10.3e}", matrix[i]) + " ";
|
||||
line += "|";
|
||||
report_->reportLineString(line);
|
||||
report_->reportLine(line);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -24,6 +24,19 @@
|
|||
|
||||
This file summarizes STA API changes for each release.
|
||||
|
||||
2026/03/12
|
||||
----------
|
||||
|
||||
The Report class used for reporting and error messages now uses std::format
|
||||
instead of printf.
|
||||
|
||||
sta::format is a wrapper for std::format that will compile on gcc8, which
|
||||
centos7 uses and does not support std::format.
|
||||
|
||||
stdstrPrint, strintPrint, stringAppend have been removed. Use sta::format.
|
||||
|
||||
reportLineString is now reportLine
|
||||
|
||||
Release 3.0.0 2025/01/03
|
||||
------------------------
|
||||
|
||||
|
|
@ -62,6 +75,8 @@ The Vector/Map/Set/UnorderedSet classes have been removed and replaced by
|
|||
the std containers. The member functions are now templated functions found
|
||||
in ContainerHelpers.hh.
|
||||
|
||||
The Graph slew_rf_count option is no longer supported.
|
||||
|
||||
Release 2.6.2 2025/03/30
|
||||
------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,135 @@ OpenSTA Timing Analyzer Release Notes
|
|||
-------------------------------------
|
||||
|
||||
This file summarizes user visible changes for each release.
|
||||
See ApiChangeLog.txt for changes to the STA api.
|
||||
|
||||
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
|
||||
----------
|
||||
|
|
|
|||
7081
doc/OpenSTA.fodt
7081
doc/OpenSTA.fodt
File diff suppressed because it is too large
Load Diff
BIN
doc/OpenSTA.pdf
BIN
doc/OpenSTA.pdf
Binary file not shown.
|
|
@ -323,28 +323,6 @@ net have wire edges between the pin vertices. Timing arc sets in the
|
|||
leaf instance timing models have corresponding edges in the graph
|
||||
between pins on the instance.
|
||||
|
||||
The Graph class constructor option slew_tr_count is used to prevent
|
||||
the grpah from reserving memory to store slews. Similarly, if the
|
||||
have_arc_delays option is false no memory is reserved for storing arc
|
||||
delay values. This is useful if an external delay calculator is used
|
||||
to annotate delays on the graph. In this case the Graph functions
|
||||
arcDelay and wireDelay should be overloaded to return delay values
|
||||
stored outside of the STA.
|
||||
|
||||
A graph with no slews or delays is constructed using:
|
||||
|
||||
Graph(this, 0, false, ap_count);
|
||||
|
||||
A graph with one slew for rising and falling edges is constructed using:
|
||||
|
||||
Graph(this, 1, true, ap_count);
|
||||
|
||||
A graph with separate rising and falling slews (the default) is
|
||||
constructed using:
|
||||
|
||||
Graph(this, 2, true, ap_count);
|
||||
|
||||
|
||||
SDC
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ foreach subdir $subdirs {
|
|||
set files [glob -nocomplain [file join $subdir "*.{cc,hh,yy,ll,i}"]]
|
||||
set files_c [concat $files_c $files]
|
||||
}
|
||||
set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|criticalError|libWarn|libError)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")}
|
||||
set warn_regexp_c {(?:(?:->critical|->warn|->fileWarn|->error|->fileError|criticalError|warn|error)\(|tclArgError\(interp,\s*)([0-9]+),.*(".+")}
|
||||
|
||||
set files_tcl {}
|
||||
foreach subdir $subdirs {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
124
graph/Graph.cc
124
graph/Graph.cc
|
|
@ -36,6 +36,7 @@
|
|||
#include "PortDirection.hh"
|
||||
#include "Network.hh"
|
||||
#include "FuncExpr.hh"
|
||||
#include "Variables.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -46,12 +47,10 @@ namespace sta {
|
|||
////////////////////////////////////////////////////////////////
|
||||
|
||||
Graph::Graph(StaState *sta,
|
||||
int slew_rf_count,
|
||||
DcalcAPIndex ap_count) :
|
||||
StaState(sta),
|
||||
vertices_(nullptr),
|
||||
edges_(nullptr),
|
||||
slew_rf_count_(slew_rf_count),
|
||||
ap_count_(ap_count),
|
||||
period_check_annotations_(nullptr),
|
||||
reg_clk_vertices_(makeVertexSet(this))
|
||||
|
|
@ -284,7 +283,7 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin,
|
|||
if (isIsolatedNet(drvrs, loads)) {
|
||||
for (auto drvr_pin : drvrs) {
|
||||
visited_drvrs.insert(drvr_pin);
|
||||
debugPrint(debug_, "graph", 1, "ignoring isolated driver %s",
|
||||
debugPrint(debug_, "graph", 1, "ignoring isolated driver {}",
|
||||
network_->pathName(drvr_pin));
|
||||
}
|
||||
return;
|
||||
|
|
@ -578,22 +577,32 @@ Graph::gateEdgeArc(const Pin *in_pin,
|
|||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
const Slew &
|
||||
Slew
|
||||
Graph::slew(const Vertex *vertex,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index)
|
||||
{
|
||||
if (slew_rf_count_) {
|
||||
const Slew *slews = vertex->slews();
|
||||
size_t slew_index = (slew_rf_count_ == 1)
|
||||
? ap_index
|
||||
: ap_index*slew_rf_count_+rf->index();
|
||||
size_t slew_index = ap_index * RiseFall::index_count + rf->index();
|
||||
const float *slews_flt = vertex->slewsFloat();
|
||||
if (variables_->pocvEnabled()) {
|
||||
const Slew *slews = std::bit_cast<const Slew*>(slews_flt);
|
||||
return slews[slew_index];
|
||||
}
|
||||
else {
|
||||
static Slew slew(0.0);
|
||||
return slew;
|
||||
else
|
||||
return slews_flt[slew_index];
|
||||
}
|
||||
|
||||
Slew
|
||||
Graph::slew(const Vertex *vertex,
|
||||
size_t index)
|
||||
{
|
||||
const float *slews_flt = vertex->slewsFloat();
|
||||
if (variables_->pocvEnabled()) {
|
||||
const Slew *slews = std::bit_cast<const Slew*>(slews_flt);
|
||||
return slews[index];
|
||||
}
|
||||
else
|
||||
return slews_flt[index];
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -602,18 +611,15 @@ Graph::setSlew(Vertex *vertex,
|
|||
DcalcAPIndex ap_index,
|
||||
const Slew &slew)
|
||||
{
|
||||
if (slew_rf_count_) {
|
||||
size_t slew_index = ap_index * RiseFall::index_count + rf->index();
|
||||
if (variables_->pocvEnabled()) {
|
||||
Slew *slews = vertex->slews();
|
||||
if (slews == nullptr) {
|
||||
int slew_count = slew_rf_count_ * ap_count_;
|
||||
slews = new Slew[slew_count];
|
||||
vertex->setSlews(slews);
|
||||
}
|
||||
size_t slew_index = (slew_rf_count_ == 1)
|
||||
? ap_index
|
||||
: ap_index*slew_rf_count_+rf->index();
|
||||
slews[slew_index] = slew;
|
||||
}
|
||||
else {
|
||||
float *slews_flt = vertex->slewsFloat();
|
||||
slews_flt[slew_index] = slew.mean();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -670,30 +676,48 @@ Graph::arcDelay(const Edge *edge,
|
|||
const TimingArc *arc,
|
||||
DcalcAPIndex ap_index) const
|
||||
{
|
||||
ArcDelay *delays = edge->arcDelays();
|
||||
size_t index = arc->index() * ap_count_ + ap_index;
|
||||
return delays[index];
|
||||
if (variables_->pocvEnabled()) {
|
||||
ArcDelay *delays = std::bit_cast<ArcDelay*>(edge->arcDelays());
|
||||
return delays[index];
|
||||
}
|
||||
else {
|
||||
const float *delays = edge->arcDelays();
|
||||
return delays[index];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Graph::setArcDelay(Edge *edge,
|
||||
const TimingArc *arc,
|
||||
DcalcAPIndex ap_index,
|
||||
ArcDelay delay)
|
||||
const ArcDelay &delay)
|
||||
{
|
||||
ArcDelay *arc_delays = edge->arcDelays();
|
||||
size_t index = arc->index() * ap_count_ + ap_index;
|
||||
arc_delays[index] = delay;
|
||||
if (variables_->pocvEnabled()) {
|
||||
ArcDelay *delays = std::bit_cast<ArcDelay*>(edge->arcDelays());
|
||||
delays[index] = delay;
|
||||
}
|
||||
else {
|
||||
float *delays = edge->arcDelays();
|
||||
delays[index] = delay.mean();
|
||||
}
|
||||
}
|
||||
|
||||
const ArcDelay &
|
||||
ArcDelay
|
||||
Graph::wireArcDelay(const Edge *edge,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index)
|
||||
{
|
||||
ArcDelay *delays = edge->arcDelays();
|
||||
size_t index = rf->index() * ap_count_ + ap_index;
|
||||
return delays[index];
|
||||
if (variables_->pocvEnabled()) {
|
||||
ArcDelay *delays = std::bit_cast<ArcDelay*>(edge->arcDelays());
|
||||
return delays[index];
|
||||
}
|
||||
else {
|
||||
const float *delays = edge->arcDelays();
|
||||
return delays[index];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -702,9 +726,15 @@ Graph::setWireArcDelay(Edge *edge,
|
|||
DcalcAPIndex ap_index,
|
||||
const ArcDelay &delay)
|
||||
{
|
||||
ArcDelay *delays = edge->arcDelays();
|
||||
size_t index = rf->index() * ap_count_ + ap_index;
|
||||
delays[index] = delay;
|
||||
if (variables_->pocvEnabled()) {
|
||||
ArcDelay *delays = std::bit_cast<ArcDelay*>(edge->arcDelays());
|
||||
delays[index] = delay;
|
||||
}
|
||||
else {
|
||||
float *delays = edge->arcDelays();
|
||||
delays[index] = delay.mean();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -788,16 +818,20 @@ void
|
|||
Graph::initSlews(Vertex *vertex)
|
||||
{
|
||||
size_t slew_count = slewCount();
|
||||
Slew *slews = new Slew[slew_count];
|
||||
vertex->setSlews(slews);
|
||||
for (size_t i = 0; i < slew_count; i++)
|
||||
slews[i] = 0.0;
|
||||
if (variables_->pocvEnabled()) {
|
||||
float *slews = std::bit_cast<float*>(new Slew[slew_count]{});
|
||||
vertex->setSlews(slews);
|
||||
}
|
||||
else {
|
||||
float *slews = new float[slew_count]{};
|
||||
vertex->setSlews(slews);
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
Graph::slewCount()
|
||||
{
|
||||
return slew_rf_count_ * ap_count_;
|
||||
return RiseFall::index_count * ap_count_;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -805,10 +839,14 @@ Graph::initArcDelays(Edge *edge)
|
|||
{
|
||||
size_t arc_count = edge->timingArcSet()->arcCount();
|
||||
size_t delay_count = arc_count * ap_count_;
|
||||
ArcDelay *arc_delays = new ArcDelay[delay_count];
|
||||
edge->setArcDelays(arc_delays);
|
||||
for (size_t i = 0; i < delay_count; i++)
|
||||
arc_delays[i] = 0.0;
|
||||
if (variables_->pocvEnabled()) {
|
||||
float *delays = std::bit_cast<float*>(new ArcDelay[delay_count]{});
|
||||
edge->setArcDelays(delays);
|
||||
}
|
||||
else {
|
||||
float *delays = new float[delay_count]{};
|
||||
edge->setArcDelays(delays);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -1040,7 +1078,7 @@ Vertex::setVisited2(bool visited)
|
|||
}
|
||||
|
||||
void
|
||||
Vertex::setSlews(Slew *slews)
|
||||
Vertex::setSlews(float *slews)
|
||||
{
|
||||
delete [] slews_;
|
||||
slews_ = slews;
|
||||
|
|
@ -1251,10 +1289,10 @@ Edge::setTimingArcSet(TimingArcSet *set)
|
|||
}
|
||||
|
||||
void
|
||||
Edge::setArcDelays(ArcDelay *arc_delays)
|
||||
Edge::setArcDelays(float *delays)
|
||||
{
|
||||
delete [] arc_delays_;
|
||||
arc_delays_ = arc_delays;
|
||||
arc_delays_ = delays;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -132,21 +132,24 @@ bool is_bidirect_driver() { return self->isBidirectDriver(); }
|
|||
int level() { return Sta::sta()->vertexLevel(self); }
|
||||
int tag_group_index() { return self->tagGroupIndex(); }
|
||||
|
||||
Slew
|
||||
float
|
||||
slew(const RiseFallBoth *rf,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
return sta->slew(self, rf, sta->scenes(), min_max);
|
||||
return delayAsFloat(sta->slew(self, rf, sta->scenes(), min_max), min_max, sta);
|
||||
}
|
||||
|
||||
Slew
|
||||
slew_scenes(const RiseFallBoth *rf,
|
||||
const SceneSeq scenes,
|
||||
const MinMax *min_max)
|
||||
std::string
|
||||
slew_scenes_string(const RiseFallBoth *rf,
|
||||
const SceneSeq scenes,
|
||||
const MinMax *min_max,
|
||||
bool report_variance,
|
||||
int digits)
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
return sta->slew(self, rf, scenes, min_max);
|
||||
Slew slew = sta->slew(self, rf, scenes, min_max);
|
||||
return delayAsString(slew, min_max, report_variance, digits, sta);
|
||||
}
|
||||
|
||||
VertexOutEdgeIterator *
|
||||
|
|
@ -223,22 +226,29 @@ arc_delays(TimingArc *arc)
|
|||
{
|
||||
Sta *sta = Sta::sta();
|
||||
FloatSeq delays;
|
||||
DcalcAPIndex ap_count = sta->dcalcAnalysisPtCount();
|
||||
for (DcalcAPIndex ap_index = 0; ap_index < ap_count; ap_index++)
|
||||
delays.push_back(delayAsFloat(sta->arcDelay(self, arc, ap_index)));
|
||||
for (Scene *scene : sta->scenes()) {
|
||||
for (const MinMax *min_max : MinMax::range()) {
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
delays.push_back(delayAsFloat(sta->arcDelay(self, arc, ap_index), min_max, sta));
|
||||
}
|
||||
}
|
||||
return delays;
|
||||
}
|
||||
|
||||
StringSeq
|
||||
arc_delay_strings(TimingArc *arc,
|
||||
bool report_variance,
|
||||
int digits)
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
StringSeq delays;
|
||||
DcalcAPIndex ap_count = sta->dcalcAnalysisPtCount();
|
||||
for (DcalcAPIndex ap_index = 0; ap_index < ap_count; ap_index++)
|
||||
delays.push_back(delayAsString(sta->arcDelay(self, arc, ap_index),
|
||||
sta, digits));
|
||||
for (Scene *scene : sta->scenes()) {
|
||||
for (const MinMax *min_max : MinMax::range()) {
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
delays.push_back(delayAsString(sta->arcDelay(self, arc, ap_index),
|
||||
min_max, report_variance, digits, sta));
|
||||
}
|
||||
}
|
||||
return delays;
|
||||
}
|
||||
|
||||
|
|
@ -255,8 +265,9 @@ arc_delay(TimingArc *arc,
|
|||
const Scene *scene,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max);
|
||||
return delayAsFloat(Sta::sta()->arcDelay(self, arc, ap_index));
|
||||
return delayAsFloat(Sta::sta()->arcDelay(self, arc, ap_index), min_max, sta);
|
||||
}
|
||||
|
||||
std::string
|
||||
|
|
@ -281,7 +292,7 @@ mode_value()
|
|||
return self->timingArcSet()->modeValue().c_str();
|
||||
}
|
||||
|
||||
const char *
|
||||
std::string
|
||||
latch_d_to_q_en()
|
||||
{
|
||||
if (self->role() == TimingRole::latchDtoQ()) {
|
||||
|
|
@ -297,9 +308,7 @@ latch_d_to_q_en()
|
|||
const RiseFall *enable_rf;
|
||||
lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf);
|
||||
if (enable_port)
|
||||
return stringPrintTmp("%s %s",
|
||||
enable_port->name(),
|
||||
enable_rf->shortName());
|
||||
return sta::format("{} {}", enable_port->name(), enable_rf->shortName());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,51 +26,64 @@
|
|||
|
||||
namespace eval sta {
|
||||
|
||||
define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]}
|
||||
define_cmd_args "report_edges" {[-from from_pin] [-to to_pin]\
|
||||
[-digits digits] [-report_variance]}
|
||||
|
||||
proc report_edges { args } {
|
||||
parse_key_args "report_edges" args keys {-from -to} flags {}
|
||||
global sta_report_default_digits
|
||||
|
||||
parse_key_args "report_edges" args keys {-from -to -digits} flags {-report_variance}
|
||||
check_argc_eq0 "report_edges" $args
|
||||
|
||||
if [info exists keys(-digits)] {
|
||||
set digits $keys(-digits)
|
||||
check_positive_integer "-digits" $digits
|
||||
} else {
|
||||
set digits $sta_report_default_digits
|
||||
}
|
||||
|
||||
set report_variance [info exists flags(-report_variance)]
|
||||
|
||||
if { [info exists keys(-from)] && [info exists keys(-to)] } {
|
||||
set from_pin [get_port_pin_error "from_pin" $keys(-from)]
|
||||
set to_pin [get_port_pin_error "to_pin" $keys(-to)]
|
||||
foreach from_vertex [$from_pin vertices] {
|
||||
foreach to_vertex [$to_pin vertices] {
|
||||
report_edges_between_ $from_vertex $to_vertex
|
||||
report_edges_between_ $from_vertex $to_vertex $digits $report_variance
|
||||
}
|
||||
}
|
||||
} elseif [info exists keys(-from)] {
|
||||
set from_pin [get_port_pin_error "from_pin" $keys(-from)]
|
||||
foreach from_vertex [$from_pin vertices] {
|
||||
report_edges_ $from_vertex out_edge_iterator \
|
||||
vertex_port_name vertex_path_name
|
||||
vertex_port_name vertex_path_name $digits $report_variance
|
||||
}
|
||||
} elseif [info exists keys(-to)] {
|
||||
set to_pin [get_port_pin_error "to_pin" $keys(-to)]
|
||||
foreach to_vertex [$to_pin vertices] {
|
||||
report_edges_ $to_vertex in_edge_iterator \
|
||||
vertex_path_name vertex_port_name
|
||||
vertex_path_name vertex_port_name $digits $report_variance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc report_edges_between_ { from_vertex to_vertex } {
|
||||
proc report_edges_between_ { from_vertex to_vertex digits report_variance } {
|
||||
set iter [$from_vertex out_edge_iterator]
|
||||
while {[$iter has_next]} {
|
||||
set edge [$iter next]
|
||||
if { [$edge to] == $to_vertex } {
|
||||
if { [$edge role] == "wire" } {
|
||||
report_edge_ $edge vertex_path_name vertex_path_name
|
||||
report_edge_ $edge vertex_path_name vertex_path_name $digits $report_variance
|
||||
} else {
|
||||
report_edge_ $edge vertex_port_name vertex_port_name
|
||||
report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance
|
||||
}
|
||||
}
|
||||
}
|
||||
$iter finish
|
||||
}
|
||||
|
||||
proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } {
|
||||
proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc \
|
||||
digits report_variance } {
|
||||
# First report edges internal to the device.
|
||||
set device_header 0
|
||||
set iter [$vertex $iter_proc]
|
||||
|
|
@ -84,7 +97,7 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } {
|
|||
}
|
||||
set device_header 1
|
||||
}
|
||||
report_edge_ $edge vertex_port_name vertex_port_name
|
||||
report_edge_ $edge vertex_port_name vertex_port_name $digits $report_variance
|
||||
}
|
||||
}
|
||||
$iter finish
|
||||
|
|
@ -94,15 +107,14 @@ proc report_edges_ { vertex iter_proc wire_from_name_proc wire_to_name_proc } {
|
|||
while {[$iter has_next]} {
|
||||
set edge [$iter next]
|
||||
if { [$edge role] == "wire" } {
|
||||
report_edge_ $edge $wire_from_name_proc $wire_to_name_proc
|
||||
report_edge_ $edge $wire_from_name_proc $wire_to_name_proc $digits $report_variance
|
||||
}
|
||||
}
|
||||
$iter finish
|
||||
}
|
||||
|
||||
proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } {
|
||||
global sta_report_default_digits
|
||||
|
||||
proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc \
|
||||
digits report_variance } {
|
||||
set latch_enable [$edge latch_d_to_q_en]
|
||||
if { $latch_enable != "" } {
|
||||
set latch_enable " enable $latch_enable"
|
||||
|
|
@ -125,7 +137,7 @@ proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } {
|
|||
}
|
||||
|
||||
foreach arc [$edge timing_arcs] {
|
||||
set delays [$edge arc_delay_strings $arc $sta_report_default_digits]
|
||||
set delays [$edge arc_delay_strings $arc $report_variance $digits]
|
||||
set delays_fmt [format_delays $delays]
|
||||
set disable_reason ""
|
||||
if { [timing_arc_disabled $edge $arc] } {
|
||||
|
|
@ -135,18 +147,6 @@ proc report_edge_ { edge vertex_from_name_proc vertex_to_name_proc } {
|
|||
}
|
||||
}
|
||||
|
||||
# Separate list elements with colons.
|
||||
proc format_times { values digits } {
|
||||
set result ""
|
||||
foreach value $values {
|
||||
if { $result != "" } {
|
||||
append result ":"
|
||||
}
|
||||
append result [format_time $value $digits]
|
||||
}
|
||||
return $result
|
||||
}
|
||||
|
||||
# Separate delay list elements with colons.
|
||||
proc format_delays { values } {
|
||||
set result ""
|
||||
|
|
|
|||
|
|
@ -119,15 +119,15 @@ public:
|
|||
ArcDcalcResult(size_t load_count);
|
||||
void setLoadCount(size_t load_count);
|
||||
ArcDelay &gateDelay() { return gate_delay_; }
|
||||
void setGateDelay(ArcDelay gate_delay);
|
||||
void setGateDelay(const ArcDelay &gate_delay);
|
||||
Slew &drvrSlew() { return drvr_slew_; }
|
||||
void setDrvrSlew(Slew drvr_slew);
|
||||
ArcDelay wireDelay(size_t load_idx) const;
|
||||
void setDrvrSlew(const Slew &drvr_slew);
|
||||
const ArcDelay &wireDelay(size_t load_idx) const;
|
||||
void setWireDelay(size_t load_idx,
|
||||
ArcDelay wire_delay);
|
||||
Slew loadSlew(size_t load_idx) const;
|
||||
const ArcDelay &wire_delay);
|
||||
const Slew &loadSlew(size_t load_idx) const;
|
||||
void setLoadSlew(size_t load_idx,
|
||||
Slew load_slew);
|
||||
const Slew &load_slew);
|
||||
|
||||
protected:
|
||||
ArcDelay gate_delay_;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "MinMax.hh"
|
||||
#include "RiseFallMinMax.hh"
|
||||
|
|
@ -207,7 +208,7 @@ public:
|
|||
~ClockEdge();
|
||||
const RiseFall *transition() const { return rf_; }
|
||||
float time() const { return time_; }
|
||||
const char *name() const { return name_; }
|
||||
const std::string &name() const { return name_; }
|
||||
int index() const { return index_; }
|
||||
ClockEdge *opposite() const;
|
||||
// Pulse width if this is the leading edge of the pulse.
|
||||
|
|
@ -221,7 +222,7 @@ private:
|
|||
|
||||
Clock *clock_;
|
||||
const RiseFall *rf_;
|
||||
const char *name_;
|
||||
std::string name_;
|
||||
float time_;
|
||||
int index_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "SdcCmdComment.hh"
|
||||
#include "SdcClass.hh"
|
||||
|
||||
|
|
@ -32,7 +34,7 @@ namespace sta {
|
|||
class ClockGroups : public SdcCmdComment
|
||||
{
|
||||
public:
|
||||
ClockGroups(const char *name,
|
||||
ClockGroups(const std::string &name,
|
||||
bool logically_exclusive,
|
||||
bool physically_exclusive,
|
||||
bool asynchronous,
|
||||
|
|
@ -40,7 +42,7 @@ public:
|
|||
const char *comment);
|
||||
~ClockGroups();
|
||||
void makeClockGroup(ClockSet *clks);
|
||||
const char *name() const { return name_; }
|
||||
const std::string &name() const { return name_; }
|
||||
ClockGroupSet *groups() { return &groups_; }
|
||||
bool logicallyExclusive() const { return logically_exclusive_; }
|
||||
bool physicallyExclusive() const { return physically_exclusive_; }
|
||||
|
|
@ -49,7 +51,7 @@ public:
|
|||
void removeClock(Clock *clk);
|
||||
|
||||
private:
|
||||
const char *name_;
|
||||
std::string name_;
|
||||
bool logically_exclusive_;
|
||||
bool physically_exclusive_;
|
||||
bool asynchronous_;
|
||||
|
|
|
|||
|
|
@ -25,10 +25,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdarg>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include "Format.hh"
|
||||
#include "Report.hh"
|
||||
#include "StringUtil.hh"
|
||||
|
||||
namespace sta {
|
||||
|
|
@ -48,10 +50,16 @@ public:
|
|||
bool check(const char *what,
|
||||
int level) const;
|
||||
int statsLevel() const { return stats_level_; }
|
||||
void reportLine(const char *what,
|
||||
const char *fmt,
|
||||
...)
|
||||
__attribute__((format (printf, 3, 4)));
|
||||
template <typename... Args>
|
||||
void report(const char *what,
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
std::string msg = sta::format("{}: {}", what,
|
||||
sta::formatRuntime(fmt, std::forward<Args>(args)...));
|
||||
std::unique_lock<std::mutex> lock(buffer_lock_);
|
||||
report_->reportLine(msg);
|
||||
}
|
||||
|
||||
protected:
|
||||
Report *report_;
|
||||
|
|
@ -63,9 +71,9 @@ protected:
|
|||
|
||||
// Inlining a varargs function would eval the args, which can
|
||||
// be expensive, so use a macro.
|
||||
#define debugPrint(debug, what, level, ...) \
|
||||
#define debugPrint(debug, what, level, fmt, ...) \
|
||||
if (debug->check(what, level)) { \
|
||||
debug->reportLine(what __VA_OPT__(,) __VA_ARGS__); \
|
||||
debug->report(what, fmt __VA_OPT__(,) __VA_ARGS__); \
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -24,27 +24,326 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "StaConfig.hh"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#if (SSTA == 1)
|
||||
// Delays are Normal PDFs.
|
||||
#include "DelayNormal1.hh"
|
||||
#elif (SSTA == 2)
|
||||
// Delays are Normal PDFs with early/late sigma.
|
||||
#include "DelayNormal2.hh"
|
||||
#else
|
||||
// Delays are floats.
|
||||
#include "DelayFloat.hh"
|
||||
#endif
|
||||
// IWYU pragma: end_exports
|
||||
#include "StaConfig.hh"
|
||||
#include "MinMax.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
class StaState;
|
||||
|
||||
class Delay
|
||||
{
|
||||
public:
|
||||
Delay();
|
||||
Delay(float mean);
|
||||
Delay(float mean,
|
||||
// std_dev^2
|
||||
float std_dev2);
|
||||
Delay(float mean,
|
||||
float mean_shift,
|
||||
// std_dev^2
|
||||
float std_dev2,
|
||||
float skewness);
|
||||
void setValues(float mean,
|
||||
float mean_shift,
|
||||
float std_dev2,
|
||||
float skewnes);
|
||||
float mean() const { return values_[0]; }
|
||||
void setMean(float mean);
|
||||
float meanShift() const { return values_[1]; }
|
||||
void setMeanShift(float mean_shift);
|
||||
float stdDev() const;
|
||||
// std_dev ^ 2
|
||||
float stdDev2() const { return values_[2]; }
|
||||
void setStdDev(float std_dev);
|
||||
float skewness() const { return values_[3]; }
|
||||
void setSkewness(float skewness);
|
||||
|
||||
void operator=(float delay);
|
||||
|
||||
private:
|
||||
std::array<float, 4> values_;
|
||||
};
|
||||
|
||||
// Delay with doubles for accumulating Delays.
|
||||
// Only a subset of operations are required for DelayDbl.
|
||||
class DelayDbl
|
||||
{
|
||||
public:
|
||||
DelayDbl();
|
||||
DelayDbl(double value);
|
||||
double mean() const { return values_[0]; }
|
||||
void setMean(double mean);
|
||||
double meanShift() const { return values_[1]; }
|
||||
// std_dev ^ 2
|
||||
double stdDev2() const { return values_[2]; }
|
||||
double stdDev() const;
|
||||
double skewness() const { return values_[3]; }
|
||||
void setValues(double mean,
|
||||
double mean_shift,
|
||||
double std_dev2,
|
||||
double skewnes);
|
||||
|
||||
void operator=(double delay);
|
||||
|
||||
private:
|
||||
std::array<double, 4> values_;
|
||||
};
|
||||
|
||||
using ArcDelay = Delay;
|
||||
using Slew = Delay;
|
||||
using Arrival = Delay;
|
||||
using Required = Delay;
|
||||
using Slack = Delay;
|
||||
|
||||
const Delay delay_zero(0.0);
|
||||
|
||||
class DelayOps
|
||||
{
|
||||
public:
|
||||
virtual ~DelayOps() {}
|
||||
virtual float stdDev2(const Delay &delay,
|
||||
const EarlyLate *early_late) const = 0;
|
||||
virtual float asFloat(const Delay &delay,
|
||||
const EarlyLate *early_late,
|
||||
const StaState *sta) const = 0;
|
||||
virtual double asFloat(const DelayDbl &delay,
|
||||
const EarlyLate *early_late,
|
||||
const StaState *sta) const = 0;
|
||||
virtual bool isZero(const Delay &delay) const = 0;
|
||||
virtual bool isInf(const Delay &delay) const = 0;
|
||||
virtual bool equal(const Delay &delay1,
|
||||
const Delay &delay2,
|
||||
const StaState *sta) const = 0;
|
||||
virtual bool less(const Delay &delay1,
|
||||
const Delay &delay2,
|
||||
const StaState *sta) const = 0;
|
||||
virtual bool less(const DelayDbl &delay1,
|
||||
const DelayDbl &delay2,
|
||||
const StaState *sta) const = 0;
|
||||
virtual bool lessEqual(const Delay &delay1,
|
||||
const Delay &delay2,
|
||||
const StaState *sta) const = 0;
|
||||
virtual bool greater(const Delay &delay1,
|
||||
const Delay &delay2,
|
||||
const StaState *sta) const = 0;
|
||||
virtual bool greaterEqual(const Delay &delay1,
|
||||
const Delay &delay2,
|
||||
const StaState *sta) const = 0;
|
||||
virtual Delay sum(const Delay &delay1,
|
||||
const Delay &delay2) const = 0;
|
||||
virtual Delay sum(const Delay &delay1,
|
||||
float delay2) const = 0;
|
||||
virtual Delay diff(const Delay &delay1,
|
||||
const Delay &delay2) const = 0;
|
||||
virtual Delay diff(const Delay &delay1,
|
||||
float delay2) const = 0;
|
||||
virtual Delay diff(float delay1,
|
||||
const Delay &delay2) const = 0;
|
||||
virtual void incr(Delay &delay1,
|
||||
const Delay &delay2) const = 0;
|
||||
virtual void incr(DelayDbl &delay1,
|
||||
const Delay &delay2) const = 0;
|
||||
virtual void decr(Delay &delay1,
|
||||
const Delay &delay2) const = 0;
|
||||
virtual void decr(DelayDbl &delay1,
|
||||
const Delay &delay2) const = 0;
|
||||
virtual Delay product(const Delay &delay1,
|
||||
float delay2) const = 0;
|
||||
virtual Delay div(float delay1,
|
||||
const Delay &delay2) const = 0;
|
||||
virtual std::string asStringVariance(const Delay &delay,
|
||||
int digits,
|
||||
const StaState *sta) const = 0;
|
||||
|
||||
};
|
||||
|
||||
void
|
||||
initDelayConstants();
|
||||
|
||||
inline float
|
||||
square(float x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
inline double
|
||||
square(double x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
inline float
|
||||
cube(float x)
|
||||
{
|
||||
return x * x * x;
|
||||
}
|
||||
|
||||
inline double
|
||||
cube(double x)
|
||||
{
|
||||
return x * x * x;
|
||||
}
|
||||
|
||||
Delay
|
||||
makeDelay(float mean,
|
||||
float mean_shift,
|
||||
float std_dev,
|
||||
float skewness);
|
||||
Delay
|
||||
makeDelay(float mean,
|
||||
float std_dev);
|
||||
Delay
|
||||
makeDelay2(float mean,
|
||||
float std_dev);
|
||||
void
|
||||
delaySetMean(Delay &delay,
|
||||
float mean);
|
||||
|
||||
std::string
|
||||
delayAsString(const Delay &delay,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include "Report.hh"
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ public:
|
|||
class ExceptionMsg : public Exception
|
||||
{
|
||||
public:
|
||||
ExceptionMsg(const char *msg,
|
||||
ExceptionMsg(const std::string &msg,
|
||||
const bool suppressed);
|
||||
virtual const char *what() const noexcept;
|
||||
virtual bool suppressed() const { return suppressed_; }
|
||||
|
|
@ -55,11 +56,11 @@ private:
|
|||
class ExceptionLine : public Exception
|
||||
{
|
||||
public:
|
||||
ExceptionLine(const char *filename,
|
||||
ExceptionLine(const std::string &filename,
|
||||
int line);
|
||||
|
||||
protected:
|
||||
const char *filename_;
|
||||
std::string filename_;
|
||||
int line_;
|
||||
};
|
||||
|
||||
|
|
@ -67,29 +68,31 @@ protected:
|
|||
class FileNotReadable : public Exception
|
||||
{
|
||||
public:
|
||||
FileNotReadable(const char *filename);
|
||||
FileNotReadable(std::string filename);
|
||||
virtual const char *what() const noexcept;
|
||||
|
||||
protected:
|
||||
const char *filename_;
|
||||
std::string filename_;
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
// Failure opening filename for writing.
|
||||
class FileNotWritable : public Exception
|
||||
{
|
||||
public:
|
||||
FileNotWritable(const char *filename);
|
||||
FileNotWritable(std::string filename);
|
||||
virtual const char *what() const noexcept;
|
||||
|
||||
protected:
|
||||
const char *filename_;
|
||||
std::string filename_;
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
// Report an error condition that should not be possible.
|
||||
// The default handler prints msg to stderr and exits.
|
||||
// The msg should NOT include a period or return.
|
||||
// Only for use in those cases where a Report object is not available.
|
||||
#define criticalError(id,msg) \
|
||||
// Only for use in those cases where a Report object is not available.
|
||||
#define criticalError(id, msg) \
|
||||
Report::defaultReport()->fileCritical(id, __FILE__, __LINE__, msg)
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Error.hh"
|
||||
|
|
@ -67,7 +68,7 @@ public:
|
|||
virtual bool isGroupPath() const { return false; }
|
||||
virtual bool isFilter() const { return false; }
|
||||
virtual ExceptionPathType type() const = 0;
|
||||
virtual const char *asString(const Network *network) const;
|
||||
virtual std::string to_string(const Network *network) const;
|
||||
ExceptionFrom *from() const { return from_; }
|
||||
ExceptionThruSeq *thrus() const { return thrus_; }
|
||||
ExceptionTo *to() const { return to_; }
|
||||
|
|
@ -127,14 +128,14 @@ public:
|
|||
virtual bool useEndClk() const { return false; }
|
||||
virtual int pathMultiplier() const { return 0; }
|
||||
virtual float delay() const { return 0.0; }
|
||||
virtual const char *name() const { return nullptr; }
|
||||
virtual std::string name() const { return ""; }
|
||||
virtual bool isDefault() const { return false; }
|
||||
virtual bool ignoreClkLatency() const { return false; }
|
||||
virtual bool breakPath() const { return false; }
|
||||
|
||||
protected:
|
||||
virtual const char *typeString() const = 0;
|
||||
const char *fromThruToString(const Network *network) const;
|
||||
std::string fromThruToString(const Network *network) const;
|
||||
void makeStates();
|
||||
|
||||
ExceptionFrom *from_;
|
||||
|
|
@ -209,7 +210,7 @@ public:
|
|||
bool own_pts) override;
|
||||
bool isPathDelay() const override { return true; }
|
||||
ExceptionPathType type() const override { return ExceptionPathType::path_delay; }
|
||||
const char *asString(const Network *network) const override;
|
||||
std::string to_string(const Network *network) const override;
|
||||
const char *typeString() const override;
|
||||
bool mergeable(ExceptionPath *exception) const override;
|
||||
bool overrides(ExceptionPath *exception) const override;
|
||||
|
|
@ -245,7 +246,7 @@ public:
|
|||
ExceptionPathType type() const override { return ExceptionPathType::multi_cycle; }
|
||||
bool matches(const MinMax *min_max,
|
||||
bool exactly) const override;
|
||||
const char *asString(const Network *network) const override;
|
||||
std::string to_string(const Network *network) const override;
|
||||
const char *typeString() const override;
|
||||
bool mergeable(ExceptionPath *exception) const override;
|
||||
bool overrides(ExceptionPath *exception) const override;
|
||||
|
|
@ -292,7 +293,7 @@ public:
|
|||
class GroupPath : public ExceptionPath
|
||||
{
|
||||
public:
|
||||
GroupPath(const char *name,
|
||||
GroupPath(const std::string &name,
|
||||
bool is_default,
|
||||
ExceptionFrom *from,
|
||||
ExceptionThruSeq *thrus,
|
||||
|
|
@ -311,11 +312,11 @@ public:
|
|||
bool overrides(ExceptionPath *exception) const override;
|
||||
int typePriority() const override;
|
||||
bool tighterThan(ExceptionPath *exception) const override;
|
||||
const char *name() const override { return name_; }
|
||||
std::string name() const override { return name_; }
|
||||
bool isDefault() const override { return is_default_; }
|
||||
|
||||
protected:
|
||||
const char *name_;
|
||||
std::string name_;
|
||||
bool is_default_;
|
||||
};
|
||||
|
||||
|
|
@ -343,7 +344,7 @@ public:
|
|||
// All pins and instance/net pins.
|
||||
virtual PinSet allPins(const Network *network) = 0;
|
||||
virtual int typePriority() const = 0;
|
||||
virtual const char *asString(const Network *network) const = 0;
|
||||
virtual std::string to_string(const Network *network) const = 0;
|
||||
virtual size_t objectCount() const = 0;
|
||||
virtual void addPin(const Pin *pin,
|
||||
const Network *network) = 0;
|
||||
|
|
@ -367,8 +368,8 @@ protected:
|
|||
// exception merging.
|
||||
size_t hash_;
|
||||
|
||||
// Maximum number of objects for asString() to show.
|
||||
static const int as_string_max_objects_;
|
||||
// Maximum number of objects for to_string() to show.
|
||||
static const int to_string_max_objects_;
|
||||
static const size_t hash_clk = 3;
|
||||
static const size_t hash_pin = 5;
|
||||
static const size_t hash_net = 7;
|
||||
|
|
@ -402,7 +403,7 @@ public:
|
|||
const Network *network) const override;
|
||||
void mergeInto(ExceptionPt *pt,
|
||||
const Network *network) override;
|
||||
const char *asString(const Network *network) const override;
|
||||
std::string to_string(const Network *network) const override;
|
||||
size_t objectCount() const override;
|
||||
void deleteClock(Clock *clk);
|
||||
void addPin(const Pin *pin,
|
||||
|
|
@ -467,7 +468,7 @@ public:
|
|||
const Network *network);
|
||||
ExceptionTo *clone(const Network *network);
|
||||
bool isTo() const override { return true; }
|
||||
const char *asString(const Network *network) const override;
|
||||
std::string to_string(const Network *network) const override;
|
||||
const RiseFallBoth *endTransition() { return end_rf_; }
|
||||
bool intersectsPts(ExceptionTo *to,
|
||||
const Network *network) const;
|
||||
|
|
@ -512,7 +513,7 @@ public:
|
|||
const Network *network);
|
||||
~ExceptionThru();
|
||||
ExceptionThru *clone(const Network *network);
|
||||
const char *asString(const Network *network) const override;
|
||||
std::string to_string(const Network *network) const override;
|
||||
bool isThru() const override { return true; }
|
||||
PinSet *pins() override { return pins_; }
|
||||
EdgePinsSet *edges() override { return edges_; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
// 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
|
||||
|
||||
// std::format is not supported in GCC 11 (e.g. Ubuntu 22.04).
|
||||
// Use fmt library as fallback when __cpp_lib_format is not defined.
|
||||
|
||||
#if defined(__cpp_lib_format) && __cpp_lib_format >= 201907L
|
||||
#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
|
||||
|
|
@ -58,13 +58,7 @@ static constexpr ObjectIdx vertex_idx_null = object_idx_null;
|
|||
class Graph : public StaState
|
||||
{
|
||||
public:
|
||||
// slew_rf_count is
|
||||
// 0 no slews
|
||||
// 1 one slew for rise/fall
|
||||
// 2 rise/fall slews
|
||||
// ap_count is the dcalc analysis point count.
|
||||
Graph(StaState *sta,
|
||||
int slew_rf_count,
|
||||
DcalcAPIndex ap_count);
|
||||
void makeGraph();
|
||||
~Graph();
|
||||
|
|
@ -97,9 +91,11 @@ public:
|
|||
// Reported slew are the same as those in the liberty tables.
|
||||
// reported_slews = measured_slews / slew_derate_from_library
|
||||
// Measured slews are between slew_lower_threshold and slew_upper_threshold.
|
||||
const Slew &slew(const Vertex *vertex,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index);
|
||||
Slew slew(const Vertex *vertex,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index);
|
||||
Slew slew(const Vertex *vertex,
|
||||
size_t index);
|
||||
void setSlew(Vertex *vertex,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index,
|
||||
|
|
@ -134,11 +130,11 @@ public:
|
|||
void setArcDelay(Edge *edge,
|
||||
const TimingArc *arc,
|
||||
DcalcAPIndex ap_index,
|
||||
ArcDelay delay);
|
||||
const ArcDelay &delay);
|
||||
// Alias for arcDelays using library wire arcs.
|
||||
const ArcDelay &wireArcDelay(const Edge *edge,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index);
|
||||
ArcDelay wireArcDelay(const Edge *edge,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index);
|
||||
void setWireArcDelay(Edge *edge,
|
||||
const RiseFall *rf,
|
||||
DcalcAPIndex ap_index,
|
||||
|
|
@ -222,7 +218,6 @@ protected:
|
|||
// driver/source (top level input, instance pin output) vertex
|
||||
// in pin_bidirect_drvr_vertex_map
|
||||
PinVertexMap pin_bidirect_drvr_vertex_map_;
|
||||
int slew_rf_count_;
|
||||
DcalcAPIndex ap_count_;
|
||||
// Sdf period check annotations.
|
||||
PeriodCheckAnnotations *period_check_annotations_;
|
||||
|
|
@ -258,8 +253,6 @@ public:
|
|||
[[nodiscard]] bool isRoot() const{ return level_ == 0; }
|
||||
[[nodiscard]] bool hasFanin() const;
|
||||
[[nodiscard]] bool hasFanout() const;
|
||||
Slew *slews() { return slews_; }
|
||||
const Slew *slews() const { return slews_; }
|
||||
Path *paths() const { return paths_; }
|
||||
Path *makePaths(uint32_t count);
|
||||
void setPaths(Path *paths);
|
||||
|
|
@ -298,14 +291,18 @@ protected:
|
|||
bool is_bidirect_drvr,
|
||||
bool is_reg_clk);
|
||||
void clear();
|
||||
void setSlews(Slew *slews);
|
||||
Slew *slews() { return std::bit_cast<Slew*>(slews_); }
|
||||
const Slew *slews() const { return std::bit_cast<const Slew*>(slews_); }
|
||||
float *slewsFloat() { return slews_; }
|
||||
const float *slewsFloat() const { return slews_; }
|
||||
void setSlews(float *slews);
|
||||
|
||||
Pin *pin_;
|
||||
EdgeId in_edges_; // Edges to this vertex.
|
||||
EdgeId out_edges_; // Edges from this vertex.
|
||||
|
||||
// Delay calc
|
||||
Slew *slews_;
|
||||
float *slews_;
|
||||
// Search
|
||||
Path *paths_;
|
||||
|
||||
|
|
@ -356,8 +353,9 @@ public:
|
|||
TimingSense sense() const;
|
||||
TimingArcSet *timingArcSet() const { return arc_set_; }
|
||||
void setTimingArcSet(TimingArcSet *set);
|
||||
ArcDelay *arcDelays() const { return arc_delays_; }
|
||||
void setArcDelays(ArcDelay *arc_delays);
|
||||
float *arcDelays() { return arc_delays_; }
|
||||
const float *arcDelays() const { return arc_delays_; }
|
||||
void setArcDelays(float *delays);
|
||||
bool delay_Annotation_Is_Incremental() const {return delay_annotation_is_incremental_;};
|
||||
void setDelayAnnotationIsIncremental(bool is_incr);
|
||||
// Edge is disabled to break combinational loops.
|
||||
|
|
@ -398,7 +396,7 @@ protected:
|
|||
EdgeId vertex_in_link_; // Vertex in edges list.
|
||||
EdgeId vertex_out_next_; // Vertex out edges doubly linked list.
|
||||
EdgeId vertex_out_prev_;
|
||||
ArcDelay *arc_delays_;
|
||||
float *arc_delays_;
|
||||
union {
|
||||
uintptr_t bits_;
|
||||
std::vector<bool> *seq_;
|
||||
|
|
|
|||
|
|
@ -245,8 +245,8 @@ protected:
|
|||
|
||||
bool annotateDelaySlew(Edge *edge,
|
||||
const TimingArc *arc,
|
||||
ArcDelay &gate_delay,
|
||||
Slew &gate_slew,
|
||||
const ArcDelay &gate_delay,
|
||||
const Slew &gate_slew,
|
||||
const Scene *scene,
|
||||
const MinMax *min_max);
|
||||
bool annotateLoadDelays(Vertex *drvr_vertex,
|
||||
|
|
|
|||
|
|
@ -33,44 +33,11 @@
|
|||
|
||||
namespace sta {
|
||||
|
||||
class InternalPowerModel;
|
||||
|
||||
using InternalPowerModels =
|
||||
std::array<std::shared_ptr<InternalPowerModel>, RiseFall::index_count>;
|
||||
|
||||
class InternalPower
|
||||
{
|
||||
public:
|
||||
InternalPower(LibertyPort *port,
|
||||
LibertyPort *related_port,
|
||||
LibertyPort *related_pg_pin,
|
||||
const std::shared_ptr<FuncExpr> &when,
|
||||
InternalPowerModels &models);
|
||||
//InternalPower(InternalPower &&other) noexcept;
|
||||
LibertyCell *libertyCell() const;
|
||||
LibertyPort *port() const { return port_; }
|
||||
LibertyPort *relatedPort() const { return related_port_; }
|
||||
FuncExpr *when() const { return when_.get(); }
|
||||
LibertyPort *relatedPgPin() const { return related_pg_pin_; }
|
||||
float power(const RiseFall *rf,
|
||||
const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap) const;
|
||||
const InternalPowerModel *model(const RiseFall *rf) const;
|
||||
|
||||
protected:
|
||||
LibertyPort *port_;
|
||||
LibertyPort *related_port_;
|
||||
LibertyPort *related_pg_pin_;
|
||||
std::shared_ptr<FuncExpr> when_;
|
||||
InternalPowerModels models_;
|
||||
};
|
||||
|
||||
class InternalPowerModel
|
||||
{
|
||||
public:
|
||||
InternalPowerModel(TableModel *model);
|
||||
~InternalPowerModel();
|
||||
InternalPowerModel();
|
||||
InternalPowerModel(std::shared_ptr<TableModel> model);
|
||||
float power(const LibertyCell *cell,
|
||||
const Pvt *pvt,
|
||||
float in_slew,
|
||||
|
|
@ -80,7 +47,7 @@ public:
|
|||
float in_slew,
|
||||
float load_cap,
|
||||
int digits) const;
|
||||
const TableModel *model() const { return model_; }
|
||||
const TableModel *model() const { return model_.get(); }
|
||||
|
||||
protected:
|
||||
void findAxisValues(float in_slew,
|
||||
|
|
@ -95,7 +62,36 @@ protected:
|
|||
bool checkAxes(const TableModel *model);
|
||||
bool checkAxis(const TableAxis *axis);
|
||||
|
||||
TableModel *model_;
|
||||
std::shared_ptr<TableModel> model_;
|
||||
};
|
||||
|
||||
using InternalPowerModels = std::array<InternalPowerModel, RiseFall::index_count>;
|
||||
|
||||
class InternalPower
|
||||
{
|
||||
public:
|
||||
InternalPower(LibertyPort *port,
|
||||
LibertyPort *related_port,
|
||||
LibertyPort *related_pg_pin,
|
||||
const std::shared_ptr<FuncExpr> &when,
|
||||
const InternalPowerModels &models);
|
||||
LibertyCell *libertyCell() const;
|
||||
LibertyPort *port() const { return port_; }
|
||||
LibertyPort *relatedPort() const { return related_port_; }
|
||||
FuncExpr *when() const { return when_.get(); }
|
||||
LibertyPort *relatedPgPin() const { return related_pg_pin_; }
|
||||
float power(const RiseFall *rf,
|
||||
const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap) const;
|
||||
const InternalPowerModel &model(const RiseFall *rf) const;
|
||||
|
||||
protected:
|
||||
LibertyPort *port_;
|
||||
LibertyPort *related_port_;
|
||||
LibertyPort *related_pg_pin_;
|
||||
std::shared_ptr<FuncExpr> when_;
|
||||
InternalPowerModels models_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -589,7 +589,7 @@ public:
|
|||
LibertyPort *related_port,
|
||||
LibertyPort *related_pg_pin,
|
||||
const std::shared_ptr<FuncExpr> &when,
|
||||
InternalPowerModels &models);
|
||||
const InternalPowerModels &models);
|
||||
void makeLeakagePower(LibertyPort *related_pg_port,
|
||||
FuncExpr *when,
|
||||
float power);
|
||||
|
|
|
|||
|
|
@ -37,14 +37,22 @@ public:
|
|||
void gateDelay(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
bool pocv_enabled,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const override;
|
||||
float &gate_delay,
|
||||
float &drvr_slew) const override;
|
||||
void gateDelayPocv(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const override;
|
||||
std::string reportGateDelay(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
bool pocv_enabled,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
int digits) const override;
|
||||
float driveResistance(const Pvt *pvt) const override;
|
||||
|
||||
|
|
@ -64,13 +72,15 @@ public:
|
|||
float from_slew,
|
||||
float to_slew,
|
||||
float related_out_cap,
|
||||
bool pocv_enabled) const override;
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode) const override;
|
||||
std::string reportCheckDelay(const Pvt *pvt,
|
||||
float from_slew,
|
||||
const char *from_slew_annotation,
|
||||
float to_slew,
|
||||
float related_out_cap,
|
||||
bool pocv_enabled,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
int digits) const override;
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -25,12 +25,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace sta {
|
||||
|
||||
// Return true if name is a bus.
|
||||
bool
|
||||
isBusName(const char *name,
|
||||
isBusName(std::string_view name,
|
||||
const char brkt_left,
|
||||
const char brkt_right,
|
||||
char escape);
|
||||
|
|
@ -43,7 +44,7 @@ isBusName(const char *name,
|
|||
// index = bit
|
||||
// Caller must delete returned bus_name string.
|
||||
void
|
||||
parseBusName(const char *name,
|
||||
parseBusName(std::string_view name,
|
||||
const char brkt_left,
|
||||
const char brkt_right,
|
||||
char escape,
|
||||
|
|
@ -53,9 +54,9 @@ parseBusName(const char *name,
|
|||
int &index);
|
||||
// Allow multiple different left/right bus brackets.
|
||||
void
|
||||
parseBusName(const char *name,
|
||||
const char *brkts_left,
|
||||
const char *brkts_right,
|
||||
parseBusName(std::string_view name,
|
||||
std::string_view brkts_left,
|
||||
std::string_view brkts_right,
|
||||
char escape,
|
||||
// Return values.
|
||||
bool &is_bus,
|
||||
|
|
@ -66,7 +67,7 @@ parseBusName(const char *name,
|
|||
// bus_name is set to null if name is not a range.
|
||||
// Caller must delete returned bus_name string.
|
||||
void
|
||||
parseBusName(const char *name,
|
||||
parseBusName(std::string_view name,
|
||||
const char brkt_left,
|
||||
const char brkt_right,
|
||||
char escape,
|
||||
|
|
@ -81,9 +82,9 @@ parseBusName(const char *name,
|
|||
// brkt_lefts and brkt_rights are corresponding strings of legal
|
||||
// bus brackets such as "[(<" and "])>".
|
||||
void
|
||||
parseBusName(const char *name,
|
||||
const char *brkts_left,
|
||||
const char *brkts_right,
|
||||
parseBusName(std::string_view name,
|
||||
std::string_view brkts_left,
|
||||
std::string_view brkts_right,
|
||||
const char escape,
|
||||
// Return values.
|
||||
bool &is_bus,
|
||||
|
|
@ -95,7 +96,7 @@ parseBusName(const char *name,
|
|||
|
||||
// Insert escapes before ch1 and ch2 in token.
|
||||
std::string
|
||||
escapeChars(const char *token,
|
||||
escapeChars(std::string_view token,
|
||||
const char ch1,
|
||||
const char ch2,
|
||||
const char escape);
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ public:
|
|||
const StaState *sta);
|
||||
Path(Vertex *vertex,
|
||||
Tag *tag,
|
||||
Arrival arrival,
|
||||
const Arrival &arrival,
|
||||
Path *prev_path,
|
||||
Edge *prev_edge,
|
||||
TimingArc *prev_arc,
|
||||
const StaState *sta);
|
||||
Path(Vertex *vertex,
|
||||
Tag *tag,
|
||||
Arrival arrival,
|
||||
const Arrival &arrival,
|
||||
Path *prev_path,
|
||||
Edge *prev_edge,
|
||||
TimingArc *prev_arc,
|
||||
|
|
@ -62,11 +62,11 @@ public:
|
|||
bool isNull() const;
|
||||
// prev_path null
|
||||
void init(Vertex *vertex,
|
||||
Arrival arrival,
|
||||
const Arrival &arrival,
|
||||
const StaState *sta);
|
||||
void init(Vertex *vertex,
|
||||
Tag *tag,
|
||||
Arrival arrival,
|
||||
const Arrival &arrival,
|
||||
Path *prev_path,
|
||||
Edge *prev_edge,
|
||||
TimingArc *prev_arc,
|
||||
|
|
@ -76,7 +76,7 @@ public:
|
|||
const StaState *sta);
|
||||
void init(Vertex *vertex,
|
||||
Tag *tag,
|
||||
Arrival arrival,
|
||||
const Arrival &arrival,
|
||||
const StaState *sta);
|
||||
|
||||
Vertex *vertex(const StaState *sta) const;
|
||||
|
|
@ -98,14 +98,12 @@ public:
|
|||
const MinMax *minMax(const StaState *sta) const;
|
||||
PathAPIndex pathAnalysisPtIndex(const StaState *sta) const;
|
||||
DcalcAPIndex dcalcAnalysisPtIndex(const StaState *sta) const;
|
||||
Arrival &arrival() { return arrival_; }
|
||||
const Arrival &arrival() const { return arrival_; }
|
||||
void setArrival(Arrival arrival);
|
||||
Required &required() { return required_; }
|
||||
const Required &required() const {return required_; }
|
||||
void setRequired(const Required &required);
|
||||
Slack slack(const StaState *sta) const;
|
||||
Slew slew(const StaState *sta) const;
|
||||
const Slew slew(const StaState *sta) const;
|
||||
// This takes the same time as prevPath and prevArc combined.
|
||||
Path *prevPath() const;
|
||||
void setPrevPath(Path *prev_path);
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public:
|
|||
virtual const char *typeName() const = 0;
|
||||
virtual int exceptPathCmp(const PathEnd *path_end,
|
||||
const StaState *sta) const;
|
||||
virtual Arrival dataArrivalTime(const StaState *sta) const;
|
||||
virtual const Arrival &dataArrivalTime(const StaState *sta) const;
|
||||
// Arrival time with source clock offset.
|
||||
Arrival dataArrivalTimeOffset(const StaState *sta) const;
|
||||
virtual Required requiredTime(const StaState *sta) const = 0;
|
||||
|
|
@ -270,11 +270,6 @@ public:
|
|||
protected:
|
||||
PathEndClkConstrained(Path *path,
|
||||
Path *clk_path);
|
||||
PathEndClkConstrained(Path *path,
|
||||
Path *clk_path,
|
||||
Crpr crpr,
|
||||
bool crpr_valid);
|
||||
|
||||
float sourceClkOffset(const ClockEdge *src_clk_edge,
|
||||
const ClockEdge *tgt_clk_edge,
|
||||
const TimingRole *check_role,
|
||||
|
|
@ -300,11 +295,6 @@ protected:
|
|||
PathEndClkConstrainedMcp(Path *path,
|
||||
Path *clk_path,
|
||||
MultiCyclePath *mcp);
|
||||
PathEndClkConstrainedMcp(Path *path,
|
||||
Path *clk_path,
|
||||
MultiCyclePath *mcp,
|
||||
Crpr crpr,
|
||||
bool crpr_valid);
|
||||
float checkMcpAdjustment(const Path *path,
|
||||
const ClockEdge *tgt_clk_edge,
|
||||
const StaState *sta) const;
|
||||
|
|
@ -341,13 +331,6 @@ public:
|
|||
virtual Delay clkSkew(const StaState *sta);
|
||||
|
||||
protected:
|
||||
PathEndCheck(Path *path,
|
||||
TimingArc *check_arc,
|
||||
Edge *check_edge,
|
||||
Path *clk_path,
|
||||
MultiCyclePath *mcp,
|
||||
Crpr crpr,
|
||||
bool crpr_valid);
|
||||
Delay sourceClkDelay(const StaState *sta) const;
|
||||
virtual Required requiredTimeNoCrpr(const StaState *sta) const;
|
||||
|
||||
|
|
@ -404,18 +387,6 @@ public:
|
|||
virtual bool ignoreClkLatency(const StaState *sta) const;
|
||||
|
||||
protected:
|
||||
PathEndLatchCheck(Path *path,
|
||||
TimingArc *check_arc,
|
||||
Edge *check_edge,
|
||||
Path *clk_path,
|
||||
Path *disable,
|
||||
MultiCyclePath *mcp,
|
||||
PathDelay *path_delay,
|
||||
Delay src_clk_arrival,
|
||||
Crpr crpr,
|
||||
bool crpr_valid);
|
||||
|
||||
private:
|
||||
Path *disable_path_;
|
||||
PathDelay *path_delay_;
|
||||
// Source clk arrival for set_max_delay -ignore_clk_latency.
|
||||
|
|
@ -450,12 +421,6 @@ public:
|
|||
const StaState *sta) const;
|
||||
|
||||
protected:
|
||||
PathEndOutputDelay(OutputDelay *output_delay,
|
||||
Path *path,
|
||||
Path *clk_path,
|
||||
MultiCyclePath *mcp,
|
||||
Crpr crpr,
|
||||
bool crpr_valid);
|
||||
Arrival tgtClkDelay(const ClockEdge *tgt_clk_edge,
|
||||
const TimingRole *check_role,
|
||||
const StaState *sta) const;
|
||||
|
|
@ -491,14 +456,6 @@ public:
|
|||
const StaState *sta) const;
|
||||
|
||||
protected:
|
||||
PathEndGatedClock(Path *gating_ref,
|
||||
Path *clk_path,
|
||||
const TimingRole *check_role,
|
||||
MultiCyclePath *mcp,
|
||||
ArcDelay margin,
|
||||
Crpr crpr,
|
||||
bool crpr_valid);
|
||||
|
||||
const TimingRole *check_role_;
|
||||
ArcDelay margin_;
|
||||
};
|
||||
|
|
@ -525,20 +482,12 @@ public:
|
|||
virtual const Path *dataClkPath() const { return data_clk_path_; }
|
||||
|
||||
protected:
|
||||
PathEndDataCheck(DataCheck *check,
|
||||
Path *data_path,
|
||||
Path *data_clk_path,
|
||||
Path *clk_path,
|
||||
MultiCyclePath *mcp,
|
||||
Crpr crpr,
|
||||
bool crpr_valid);
|
||||
Path *clkPath(Path *path,
|
||||
const StaState *sta);
|
||||
Arrival requiredTimeNoCrpr(const StaState *sta) const;
|
||||
// setup uses zero cycle default
|
||||
virtual int setupDefaultCycles() const { return 0; }
|
||||
|
||||
private:
|
||||
Path *data_clk_path_;
|
||||
DataCheck *check_;
|
||||
};
|
||||
|
|
@ -588,15 +537,6 @@ public:
|
|||
virtual bool ignoreClkLatency(const StaState *sta) const;
|
||||
|
||||
protected:
|
||||
PathEndPathDelay(PathDelay *path_delay,
|
||||
Path *path,
|
||||
Path *clk_path,
|
||||
TimingArc *check_arc,
|
||||
Edge *check_edge,
|
||||
OutputDelay *output_delay,
|
||||
Arrival src_clk_arrival,
|
||||
Crpr crpr,
|
||||
bool crpr_valid);
|
||||
void findSrcClkArrival(const StaState *sta);
|
||||
|
||||
PathDelay *path_delay_;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include "BoundedHeap.hh"
|
||||
#include "SdcClass.hh"
|
||||
#include "StaState.hh"
|
||||
#include "SearchClass.hh"
|
||||
|
|
@ -43,7 +42,7 @@ class PathEndVisitor;
|
|||
|
||||
using PathGroupIterator = PathEndSeq::iterator;
|
||||
using PathGroupClkMap = std::map<const Clock*, PathGroup*>;
|
||||
using PathGroupNamedMap = std::map<const char*, PathGroup*, CharPtrLess>;
|
||||
using PathGroupNamedMap = std::map<std::string, PathGroup*>;
|
||||
using PathGroupSeq = std::vector<PathGroup*>;
|
||||
|
||||
// A collection of PathEnds grouped and sorted for reporting.
|
||||
|
|
@ -70,7 +69,7 @@ public:
|
|||
~PathGroup();
|
||||
const std::string &name() const { return name_; }
|
||||
const MinMax *minMax() const { return min_max_;}
|
||||
PathEndSeq pathEnds() const;
|
||||
PathEndSeq pathEnds() const { return path_ends_; }
|
||||
void insert(PathEnd *path_end);
|
||||
// Push group_path_count into path_ends.
|
||||
void pushEnds(PathEndSeq &path_ends);
|
||||
|
|
@ -93,6 +92,9 @@ protected:
|
|||
bool cmp_slack,
|
||||
const MinMax *min_max,
|
||||
const StaState *sta);
|
||||
void ensureSortedMaxPaths();
|
||||
void prune();
|
||||
void sort();
|
||||
|
||||
std::string name_;
|
||||
int group_path_count_;
|
||||
|
|
@ -101,9 +103,11 @@ protected:
|
|||
bool unique_edges_;
|
||||
float slack_min_;
|
||||
float slack_max_;
|
||||
PathEndSeq path_ends_;
|
||||
const MinMax *min_max_;
|
||||
bool cmp_slack_;
|
||||
BoundedHeap<PathEnd*, PathEndLess> heap_;
|
||||
float threshold_;
|
||||
|
||||
std::mutex lock_;
|
||||
const StaState *sta_;
|
||||
};
|
||||
|
|
@ -136,7 +140,7 @@ public:
|
|||
bool unconstrained_paths,
|
||||
// Return value.
|
||||
PathEndSeq &path_ends);
|
||||
PathGroup *findPathGroup(const char *name,
|
||||
PathGroup *findPathGroup(const std::string &name,
|
||||
const MinMax *min_max) const;
|
||||
PathGroup *findPathGroup(const Clock *clock,
|
||||
const MinMax *min_max) const;
|
||||
|
|
@ -187,7 +191,7 @@ protected:
|
|||
bool gated_clk,
|
||||
bool unconstrained,
|
||||
const MinMax *min_max);
|
||||
bool reportGroup(const char *group_name,
|
||||
bool reportGroup(const std::string &group_name,
|
||||
StringSet &group_names) const;
|
||||
static GroupPath *groupPathTo(const PathEnd *path_end,
|
||||
const StaState *sta);
|
||||
|
|
|
|||
|
|
@ -22,19 +22,15 @@
|
|||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "Machine.hh"
|
||||
#include "StringUtil.hh"
|
||||
#include "Units.hh"
|
||||
#include "StaState.hh"
|
||||
#include "Delay.hh"
|
||||
#pragma once
|
||||
|
||||
namespace sta {
|
||||
|
||||
enum class PocvMode { scalar, normal, skew_normal };
|
||||
|
||||
const char *
|
||||
delayAsString(const Delay &delay,
|
||||
const StaState *sta)
|
||||
{
|
||||
return delayAsString(delay, sta, sta->units()->timeUnit()->digits());
|
||||
}
|
||||
pocvModeName(PocvMode mode);
|
||||
PocvMode
|
||||
findPocvMode(const char *mode_name);
|
||||
|
||||
} // namespace
|
||||
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#pragma once
|
||||
|
|
@ -27,15 +27,23 @@
|
|||
#include <stdio.h>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "Machine.hh" // __attribute__
|
||||
#include "Machine.hh" // __attribute__
|
||||
#include "Format.hh"
|
||||
|
||||
struct Tcl_Interp;
|
||||
|
||||
namespace sta {
|
||||
|
||||
// Throws ExceptionMsg - implemented in Report.cc to avoid circular include with
|
||||
// Error.hh
|
||||
void
|
||||
reportThrowExceptionMsg(const std::string &msg,
|
||||
bool suppressed);
|
||||
|
||||
// Output streams used for printing.
|
||||
// This is a wrapper for all printing. It supports logging output to
|
||||
// a file and redirection of command output to a file.
|
||||
|
|
@ -45,74 +53,98 @@ public:
|
|||
Report();
|
||||
virtual ~Report();
|
||||
|
||||
// Print line with return.
|
||||
virtual void reportLine(const char *fmt, ...)
|
||||
__attribute__((format (printf, 2, 3)));
|
||||
virtual void reportLineString(const char *line);
|
||||
virtual void reportLineString(const std::string &line);
|
||||
virtual void reportLine(const std::string &line);
|
||||
virtual void reportBlankLine();
|
||||
|
||||
// Print formatted line using std::format (C++20).
|
||||
template <typename... Args>
|
||||
void report(std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
reportLine(sta::vformat(fmt, sta::make_format_args(args...)));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// Report warning.
|
||||
virtual void warn(int id,
|
||||
const char *fmt, ...)
|
||||
__attribute__((format (printf, 3, 4)));
|
||||
virtual void vwarn(int id,
|
||||
const char *fmt,
|
||||
va_list args);
|
||||
template <typename... Args>
|
||||
void warn(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
if (!isSuppressed(id)) {
|
||||
reportLine(sta::format(
|
||||
"Warning {}: {}", id, sta::vformat(fmt, sta::make_format_args(args...))));
|
||||
}
|
||||
}
|
||||
// Report warning in a file.
|
||||
virtual void fileWarn(int id,
|
||||
const char *filename,
|
||||
int line,
|
||||
const char *fmt, ...)
|
||||
__attribute__((format (printf, 5, 6)));
|
||||
virtual void vfileWarn(int id,
|
||||
const char *filename,
|
||||
int line,
|
||||
const char *fmt,
|
||||
va_list args);
|
||||
template <typename... Args>
|
||||
void fileWarn(int id,
|
||||
std::string_view filename,
|
||||
int line,
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
if (!isSuppressed(id)) {
|
||||
reportLine(
|
||||
sta::format("Warning {}: {} line {}, {}", id, filename, line,
|
||||
sta::vformat(fmt, sta::make_format_args(args...))));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void error(int id,
|
||||
const char *fmt, ...)
|
||||
__attribute__((format (printf, 3, 4)));
|
||||
virtual void verror(int id,
|
||||
const char *fmt,
|
||||
va_list args);
|
||||
template <typename... Args>
|
||||
void error(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
std::string msg = sta::vformat(fmt, sta::make_format_args(args...));
|
||||
reportThrowExceptionMsg(sta::format("{} {}", id, msg), isSuppressed(id));
|
||||
}
|
||||
// Report error in a file.
|
||||
virtual void fileError(int id,
|
||||
const char *filename,
|
||||
int line,
|
||||
const char *fmt, ...)
|
||||
__attribute__((format (printf, 5, 6)));
|
||||
virtual void vfileError(int id,
|
||||
const char *filename,
|
||||
int line,
|
||||
const char *fmt,
|
||||
va_list args);
|
||||
template <typename... Args>
|
||||
void fileError(int id,
|
||||
std::string_view filename,
|
||||
int line,
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
const std::string msg = sta::vformat(fmt, sta::make_format_args(args...));
|
||||
reportThrowExceptionMsg(sta::format("{} {} line {}, {}", id, filename, line, msg),
|
||||
isSuppressed(id));
|
||||
}
|
||||
|
||||
// Critical.
|
||||
// Critical.
|
||||
// Report error condition that should not be possible or that prevents execution.
|
||||
// The default handler prints msg to stderr and exits.
|
||||
virtual void critical(int id,
|
||||
const char *fmt,
|
||||
...)
|
||||
__attribute__((format (printf, 3, 4)));
|
||||
virtual void fileCritical(int id,
|
||||
const char *filename,
|
||||
int line,
|
||||
const char *fmt,
|
||||
...)
|
||||
__attribute__((format (printf, 5, 6)));
|
||||
template <typename... Args>
|
||||
void critical(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
reportLine(sta::format("Critical {}: {}", id,
|
||||
sta::vformat(fmt, sta::make_format_args(args...))));
|
||||
exit(1);
|
||||
}
|
||||
template <typename... Args>
|
||||
void fileCritical(int id,
|
||||
std::string_view filename,
|
||||
int line,
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
reportLine(sta::format("Critical {}: {} line {}, {}", id, filename, line,
|
||||
sta::vformat(fmt, sta::make_format_args(args...))));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Log output to filename until logEnd is called.
|
||||
virtual void logBegin(const char *filename);
|
||||
virtual void logBegin(std::string_view filename);
|
||||
virtual void logEnd();
|
||||
|
||||
// Redirect output to filename until redirectFileEnd is called.
|
||||
virtual void redirectFileBegin(const char *filename);
|
||||
virtual void redirectFileBegin(std::string_view filename);
|
||||
// Redirect append output to filename until redirectFileEnd is called.
|
||||
virtual void redirectFileAppendBegin(const char *filename);
|
||||
virtual void redirectFileAppendBegin(std::string_view filename);
|
||||
virtual void redirectFileEnd();
|
||||
// Redirect output to a string until redirectStringEnd is called.
|
||||
virtual void redirectStringBegin();
|
||||
|
|
@ -139,9 +171,7 @@ protected:
|
|||
// Return the number of characters written.
|
||||
virtual size_t printConsole(const char *buffer,
|
||||
size_t length);
|
||||
void printToBuffer(const char *fmt,
|
||||
...)
|
||||
__attribute__((format (printf, 2, 3)));
|
||||
void printToBuffer(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
|
||||
void printToBuffer(const char *fmt,
|
||||
va_list args);
|
||||
|
|
@ -169,4 +199,4 @@ protected:
|
|||
friend class Debug;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -44,20 +44,20 @@ class ReportTcl : public Report
|
|||
public:
|
||||
ReportTcl();
|
||||
virtual ~ReportTcl();
|
||||
virtual void logBegin(const char *filename);
|
||||
virtual void logEnd();
|
||||
virtual void redirectFileBegin(const char *filename);
|
||||
virtual void redirectFileAppendBegin(const char *filename);
|
||||
virtual void redirectFileEnd();
|
||||
virtual void redirectStringBegin();
|
||||
virtual const char *redirectStringEnd();
|
||||
void logBegin(std::string_view filename) override;
|
||||
void logEnd() override;
|
||||
void redirectFileBegin(std::string_view filename) override;
|
||||
void redirectFileAppendBegin(std::string_view filename) override;
|
||||
void redirectFileEnd() override;
|
||||
void redirectStringBegin() override;
|
||||
const char *redirectStringEnd() override;
|
||||
// This must be called after the Tcl interpreter has been constructed.
|
||||
// It makes the encapsulated channels.
|
||||
virtual void setTclInterp(Tcl_Interp *interp);
|
||||
void setTclInterp(Tcl_Interp *interp) override;
|
||||
|
||||
protected:
|
||||
virtual size_t printConsole(const char *buffer,
|
||||
size_t length);
|
||||
size_t printConsole(const char *buffer,
|
||||
size_t length) override;
|
||||
void flush();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -179,11 +179,11 @@ using InstDeratingFactorsMap = std::map<const Instance*, DeratingFactorsCell*>;
|
|||
using CellDeratingFactorsMap = std::map<const LibertyCell*, DeratingFactorsCell*>;
|
||||
using ClockGroupsSet = std::set<ClockGroups*>;
|
||||
using ClockGroupsClkMap = std::map<const Clock*, ClockGroupsSet*>;
|
||||
using ClockGroupsNameMap = std::map<const char*, ClockGroups*, CharPtrLess>;
|
||||
using ClockGroupsNameMap = std::map<std::string, ClockGroups*>;
|
||||
using ClockSenseMap = std::map<PinClockPair, ClockSense, PinClockPairLess>;
|
||||
using ClkHpinDisables = std::set<ClkHpinDisable*, ClkHpinDisableLess>;
|
||||
using GroupPathSet = std::set<GroupPath*, ExceptionPathLess>;
|
||||
using GroupPathMap = std::map<const char*, GroupPathSet*, CharPtrLess>;
|
||||
using GroupPathMap = std::map<std::string, GroupPathSet*>;
|
||||
using ClockPairSet = std::set<ClockPair, ClockPairLess>;
|
||||
using NetVoltageMap = std::map<const Net*, MinMaxFloatValues>;
|
||||
|
||||
|
|
@ -499,7 +499,7 @@ public:
|
|||
Clock *to_clk,
|
||||
const RiseFallBoth *to_rf,
|
||||
const SetupHoldAll *setup_hold);
|
||||
ClockGroups *makeClockGroups(const char *name,
|
||||
ClockGroups *makeClockGroups(const std::string &name,
|
||||
bool logically_exclusive,
|
||||
bool physically_exclusive,
|
||||
bool asynchronous,
|
||||
|
|
@ -507,11 +507,13 @@ public:
|
|||
const char *comment);
|
||||
void makeClockGroup(ClockGroups *clk_groups,
|
||||
ClockSet *clks);
|
||||
void removeClockGroups(const char *name);
|
||||
// nullptr name removes all.
|
||||
void removeClockGroupsLogicallyExclusive(const char *name);
|
||||
void removeClockGroupsPhysicallyExclusive(const char *name);
|
||||
void removeClockGroupsAsynchronous(const char *name);
|
||||
void removeClockGroups(const std::string &name);
|
||||
void removeClockGroupsLogicallyExclusive();
|
||||
void removeClockGroupsLogicallyExclusive(const std::string &name);
|
||||
void removeClockGroupsPhysicallyExclusive();
|
||||
void removeClockGroupsPhysicallyExclusive(const std::string &name);
|
||||
void removeClockGroupsAsynchronous();
|
||||
void removeClockGroupsAsynchronous(const std::string &name);
|
||||
bool sameClockGroup(const Clock *clk1,
|
||||
const Clock *clk2) const;
|
||||
// Clocks explicitly excluded by set_clock_group.
|
||||
|
|
@ -756,7 +758,7 @@ public:
|
|||
ExceptionThruSeq *thrus,
|
||||
ExceptionTo *to,
|
||||
const MinMaxAll *min_max);
|
||||
void makeGroupPath(const char *name,
|
||||
void makeGroupPath(const std::string &name,
|
||||
bool is_default,
|
||||
ExceptionFrom *from,
|
||||
ExceptionThruSeq *thrus,
|
||||
|
|
@ -1266,7 +1268,7 @@ protected:
|
|||
void makeClkGroupExclusions(ClockGroupSet *groups);
|
||||
void makeClkGroupSame(ClockGroup *group);
|
||||
void clearClkGroupExclusions();
|
||||
char *makeClockGroupsName();
|
||||
std::string makeClockGroupsName();
|
||||
void setClockSense(const Pin *pin,
|
||||
const Clock *clk,
|
||||
ClockSense sense);
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ protected:
|
|||
const PatternMatch *pattern,
|
||||
InstanceSeq &matches) const;
|
||||
|
||||
const char *staToSdc(const char *sta_name) const;
|
||||
const char *staToSdc(std::string_view sta_name) const;
|
||||
};
|
||||
|
||||
// Encapsulate a network to map names to/from the sdc namespace.
|
||||
|
|
|
|||
|
|
@ -434,19 +434,21 @@ public:
|
|||
const RiseFallBoth *to_rf,
|
||||
const SetupHoldAll *setup_hold,
|
||||
Sdc *sdc);
|
||||
ClockGroups *makeClockGroups(const char *name,
|
||||
ClockGroups *makeClockGroups(const std::string &name,
|
||||
bool logically_exclusive,
|
||||
bool physically_exclusive,
|
||||
bool asynchronous,
|
||||
bool allow_paths,
|
||||
const char *comment,
|
||||
Sdc *sdc);
|
||||
// nullptr name removes all.
|
||||
void removeClockGroupsLogicallyExclusive(const char *name,
|
||||
void removeClockGroupsLogicallyExclusive(Sdc *sdc);
|
||||
void removeClockGroupsLogicallyExclusive(const std::string &name,
|
||||
Sdc *sdc);
|
||||
void removeClockGroupsPhysicallyExclusive(const char *name,
|
||||
void removeClockGroupsPhysicallyExclusive(Sdc *sdc);
|
||||
void removeClockGroupsPhysicallyExclusive(const std::string &name,
|
||||
Sdc *sdc);
|
||||
void removeClockGroupsAsynchronous(const char *name,
|
||||
void removeClockGroupsAsynchronous(Sdc *sdc);
|
||||
void removeClockGroupsAsynchronous(const std::string &name,
|
||||
Sdc *sdc);
|
||||
void makeClockGroup(ClockGroups *clk_groups,
|
||||
ClockSet *clks,
|
||||
|
|
@ -640,7 +642,7 @@ public:
|
|||
float delay,
|
||||
const char *comment,
|
||||
Sdc *sdc);
|
||||
void makeGroupPath(const char *name,
|
||||
void makeGroupPath(const std::string &name,
|
||||
bool is_default,
|
||||
ExceptionFrom *from,
|
||||
ExceptionThruSeq *thrus,
|
||||
|
|
@ -982,11 +984,11 @@ public:
|
|||
bool report_cap,
|
||||
bool report_slew,
|
||||
bool report_fanout,
|
||||
bool report_variation,
|
||||
bool report_src_attr);
|
||||
ReportField *findReportPathField(const char *name);
|
||||
void setReportPathDigits(int digits);
|
||||
void setReportPathNoSplit(bool no_split);
|
||||
void setReportPathSigmas(bool report_sigmas);
|
||||
void reportPathEnd(PathEnd *end);
|
||||
void reportPathEnds(PathEndSeq *ends);
|
||||
ReportPath *reportPath() { return report_path_; }
|
||||
|
|
@ -998,7 +1000,7 @@ public:
|
|||
const SetupHold *setup_hold,
|
||||
bool include_internal_latency,
|
||||
int digits);
|
||||
float findWorstClkSkew(const SetupHold *setup_hold,
|
||||
Delay findWorstClkSkew(const SetupHold *setup_hold,
|
||||
bool include_internal_latency);
|
||||
|
||||
void reportClkLatency(ConstClockSeq &clks,
|
||||
|
|
@ -1131,12 +1133,15 @@ public:
|
|||
|
||||
void reportArrivalWrtClks(const Pin *pin,
|
||||
const Scene *scene,
|
||||
bool report_variance,
|
||||
int digits);
|
||||
void reportRequiredWrtClks(const Pin *pin,
|
||||
const Scene *scene,
|
||||
bool report_variance,
|
||||
int digits);
|
||||
void reportSlackWrtClks(const Pin *pin,
|
||||
const Scene *scene,
|
||||
bool report_variance,
|
||||
int digits);
|
||||
|
||||
Slew slew(Vertex *vertex,
|
||||
|
|
@ -1144,9 +1149,9 @@ public:
|
|||
const SceneSeq &scenes,
|
||||
const MinMax *min_max);
|
||||
|
||||
ArcDelay arcDelay(Edge *edge,
|
||||
TimingArc *arc,
|
||||
DcalcAPIndex ap_index);
|
||||
const ArcDelay arcDelay(Edge *edge,
|
||||
TimingArc *arc,
|
||||
DcalcAPIndex ap_index);
|
||||
// True if the timing arc has been back-annotated.
|
||||
bool arcDelayAnnotated(Edge *edge,
|
||||
TimingArc *arc,
|
||||
|
|
@ -1408,12 +1413,13 @@ public:
|
|||
// TCL variable sta_crpr_mode.
|
||||
CrprMode crprMode() const;
|
||||
void setCrprMode(CrprMode mode);
|
||||
// TCL variable sta_pocv_enabled.
|
||||
// TCL variable sta_pocv_mode.
|
||||
// Parametric on chip variation (statisical sta).
|
||||
bool pocvEnabled() const;
|
||||
void setPocvEnabled(bool enabled);
|
||||
PocvMode pocvMode() const;
|
||||
void setPocvMode(PocvMode mode);
|
||||
// Number of std deviations from mean to use for normal distributions.
|
||||
void setSigmaFactor(float factor);
|
||||
float pocvQuantile();
|
||||
void setPocvQuantile(float quantile);
|
||||
// TCL variable sta_propagate_gated_clock_enable.
|
||||
// Propagate gated clock enable arrivals.
|
||||
bool propagateGatedClockEnable() const;
|
||||
|
|
@ -1510,17 +1516,20 @@ protected:
|
|||
|
||||
void reportDelaysWrtClks(const Pin *pin,
|
||||
const Scene *scene,
|
||||
bool report_variance,
|
||||
int digits,
|
||||
bool find_required,
|
||||
PathDelayFunc get_path_delay);
|
||||
void reportDelaysWrtClks(Vertex *vertex,
|
||||
const Scene *scene,
|
||||
bool report_variance,
|
||||
int digits,
|
||||
bool find_required,
|
||||
PathDelayFunc get_path_delay);
|
||||
void reportDelaysWrtClks(Vertex *vertex,
|
||||
const ClockEdge *clk_edge,
|
||||
const Scene *scene,
|
||||
bool report_variance,
|
||||
int digits,
|
||||
PathDelayFunc get_path_delay);
|
||||
RiseFallMinMaxDelay findDelaysWrtClks(Vertex *vertex,
|
||||
|
|
@ -1530,6 +1539,7 @@ protected:
|
|||
std::string formatDelay(const RiseFall *rf,
|
||||
const MinMax *min_max,
|
||||
const RiseFallMinMaxDelay &delays,
|
||||
bool report_variance,
|
||||
int digits);
|
||||
|
||||
void connectDrvrPinAfter(Vertex *vertex);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class GraphDelayCalc;
|
|||
class Latches;
|
||||
class DispatchQueue;
|
||||
class Variables;
|
||||
class DelayOps;
|
||||
|
||||
using ModeSeq = std::vector<Mode*>;
|
||||
using ModeSet = std::set<Mode*>;
|
||||
|
|
@ -96,10 +97,10 @@ public:
|
|||
GraphDelayCalc *graphDelayCalc() const { return graph_delay_calc_; }
|
||||
Search *search() { return search_; }
|
||||
Search *search() const { return search_; }
|
||||
const DelayOps *delayOps() const { return delay_ops_; }
|
||||
Latches *latches() { return latches_; }
|
||||
Latches *latches() const { return latches_; }
|
||||
unsigned threadCount() const { return thread_count_; }
|
||||
float sigmaFactor() const { return sigma_factor_; }
|
||||
bool crprActive(const Mode *mode) const;
|
||||
Variables *variables() { return variables_; }
|
||||
const Variables *variables() const { return variables_; }
|
||||
|
|
@ -133,11 +134,11 @@ protected:
|
|||
ArcDelayCalc *arc_delay_calc_;
|
||||
GraphDelayCalc *graph_delay_calc_;
|
||||
Search *search_;
|
||||
DelayOps *delay_ops_;
|
||||
Latches *latches_;
|
||||
Variables *variables_;
|
||||
int thread_count_;
|
||||
DispatchQueue *dispatch_queue_;
|
||||
float sigma_factor_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -143,14 +143,6 @@ public:
|
|||
char *
|
||||
stringCopy(const char *str);
|
||||
|
||||
inline void
|
||||
stringAppend(char *&str1,
|
||||
const char *str2)
|
||||
{
|
||||
strcpy(str1, str2);
|
||||
str1 += strlen(str2);
|
||||
}
|
||||
|
||||
void
|
||||
stringDeleteCheck(const char *str);
|
||||
|
||||
|
|
@ -164,32 +156,6 @@ stringDelete(const char *str)
|
|||
bool
|
||||
isDigits(const char *str);
|
||||
|
||||
// Print to a new string.
|
||||
// Caller owns returned string.
|
||||
char *
|
||||
stringPrint(const char *fmt,
|
||||
...) __attribute__((format (printf, 1, 2)));
|
||||
std::string
|
||||
stdstrPrint(const char *fmt,
|
||||
...) __attribute__((format (printf, 1, 2)));
|
||||
char *
|
||||
stringPrintArgs(const char *fmt,
|
||||
va_list args);
|
||||
void
|
||||
stringPrint(std::string &str,
|
||||
const char *fmt,
|
||||
...) __attribute__((format (printf, 2, 3)));
|
||||
// Formated append to std::string.
|
||||
void
|
||||
stringAppend(std::string &str,
|
||||
const char *fmt,
|
||||
...) __attribute__((format (printf, 2, 3)));
|
||||
|
||||
// Print to a temporary string.
|
||||
char *
|
||||
stringPrintTmp(const char *fmt,
|
||||
...) __attribute__((format (printf, 1, 2)));
|
||||
|
||||
char *
|
||||
makeTmpString(size_t length);
|
||||
char *
|
||||
|
|
|
|||
|
|
@ -33,12 +33,14 @@
|
|||
#include "Transition.hh"
|
||||
#include "LibertyClass.hh"
|
||||
#include "TimingModel.hh"
|
||||
#include "Variables.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
class Unit;
|
||||
class Units;
|
||||
class Report;
|
||||
class TableModels;
|
||||
class Table;
|
||||
class TableModel;
|
||||
class TableAxis;
|
||||
|
|
@ -63,43 +65,41 @@ class GateTableModel : public GateTimingModel
|
|||
{
|
||||
public:
|
||||
GateTableModel(LibertyCell *cell,
|
||||
TableModel *delay_model,
|
||||
TableModelsEarlyLate delay_sigma_models,
|
||||
TableModel *slew_model,
|
||||
TableModelsEarlyLate slew_sigma_models,
|
||||
TableModels *delay_models,
|
||||
TableModels *slew_models,
|
||||
ReceiverModelPtr receiver_model,
|
||||
OutputWaveforms *output_waveforms);
|
||||
GateTableModel(LibertyCell *cell,
|
||||
TableModel *delay_model,
|
||||
TableModel *slew_model);
|
||||
TableModels *delay_models,
|
||||
TableModels *slew_models);
|
||||
~GateTableModel() override;
|
||||
void gateDelay(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
bool pocv_enabled,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const override;
|
||||
// deprecated 2024-01-07
|
||||
// related_out_cap arg removed.
|
||||
void gateDelay(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
float related_out_cap,
|
||||
bool pocv_enabled,
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const __attribute__ ((deprecated));
|
||||
float &gate_delay,
|
||||
float &drvr_slew) const override;
|
||||
// Fill in pocv parameters in gate_delay, drvr_slew.
|
||||
void gateDelayPocv(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const override;
|
||||
std::string reportGateDelay(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
bool pocv_enabled,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
int digits) const override;
|
||||
float driveResistance(const Pvt *pvt) const override;
|
||||
|
||||
const TableModel *delayModel() const { return delay_model_.get(); }
|
||||
const TableModel *slewModel() const { return slew_model_.get(); }
|
||||
const TableModel *delaySigmaModel(const EarlyLate *el) const;
|
||||
const TableModel *slewSigmaModel(const EarlyLate *el) const;
|
||||
const TableModels *delayModels() const { return delay_models_.get(); }
|
||||
const TableModel *delayModel() const;
|
||||
const TableModels *slewModels() const { return slew_models_.get(); }
|
||||
const TableModel *slewModel() const;
|
||||
const ReceiverModel *receiverModel() const { return receiver_model_.get(); }
|
||||
OutputWaveforms *outputWaveforms() const { return output_waveforms_.get(); }
|
||||
// Check the axes before making the model.
|
||||
|
|
@ -138,10 +138,8 @@ protected:
|
|||
float &axis_value3) const;
|
||||
static bool checkAxis(const TableAxis *axis);
|
||||
|
||||
std::unique_ptr<TableModel> delay_model_;
|
||||
TableModelsEarlyLate delay_sigma_models_;
|
||||
std::unique_ptr<TableModel> slew_model_;
|
||||
TableModelsEarlyLate slew_sigma_models_;
|
||||
std::unique_ptr<TableModels> delay_models_;
|
||||
std::unique_ptr<TableModels> slew_models_;
|
||||
ReceiverModelPtr receiver_model_;
|
||||
std::unique_ptr<OutputWaveforms> output_waveforms_;
|
||||
};
|
||||
|
|
@ -150,25 +148,24 @@ class CheckTableModel : public CheckTimingModel
|
|||
{
|
||||
public:
|
||||
CheckTableModel(LibertyCell *cell,
|
||||
TableModel *model,
|
||||
TableModelsEarlyLate sigma_models);
|
||||
CheckTableModel(LibertyCell *cell,
|
||||
TableModel *model);
|
||||
TableModels *check_models);
|
||||
~CheckTableModel() override;
|
||||
ArcDelay checkDelay(const Pvt *pvt,
|
||||
float from_slew,
|
||||
float to_slew,
|
||||
float related_out_cap,
|
||||
bool pocv_enabled) const override;
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode) const override;
|
||||
std::string reportCheckDelay(const Pvt *pvt,
|
||||
float from_slew,
|
||||
const char *from_slew_annotation,
|
||||
float to_slew,
|
||||
float related_out_cap,
|
||||
bool pocv_enabled,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
int digits) const override;
|
||||
const TableModel *model() const { return model_.get(); }
|
||||
const TableModel *sigmaModel(const EarlyLate *el) const;
|
||||
const TableModels *checkModels() const { return check_models_.get(); }
|
||||
const TableModel *checkModel() const;
|
||||
|
||||
// Check the axes before making the model.
|
||||
// Return true if the model axes are supported.
|
||||
|
|
@ -202,8 +199,7 @@ protected:
|
|||
int digits) const;
|
||||
static bool checkAxis(const TableAxis *axis);
|
||||
|
||||
std::unique_ptr<TableModel> model_;
|
||||
TableModelsEarlyLate sigma_models_;
|
||||
std::unique_ptr<TableModels> check_models_;
|
||||
};
|
||||
|
||||
class TableAxis
|
||||
|
|
@ -311,6 +307,8 @@ public:
|
|||
|
||||
private:
|
||||
void clear();
|
||||
float findValueOrder2(float axis_value1, float axis_value2) const;
|
||||
float findValueOrder3(float axis_value1, float axis_value2, float axis_value3) const;
|
||||
std::string reportValueOrder0(const char *result_name,
|
||||
const char *comment1,
|
||||
const Unit *table_unit,
|
||||
|
|
@ -408,6 +406,34 @@ protected:
|
|||
bool is_scaled_:1;
|
||||
};
|
||||
|
||||
// cell/transition/check nldm/ocv/lvf models for one rise/fall edge.
|
||||
class TableModels
|
||||
{
|
||||
public:
|
||||
TableModels();
|
||||
TableModels(TableModel *model);
|
||||
~TableModels();
|
||||
TableModel *model() const { return model_.get(); }
|
||||
void setModel(TableModel *model);
|
||||
TableModel *sigma(const EarlyLate *early_late) const;
|
||||
void setSigma(TableModel *table,
|
||||
const EarlyLate *early_late);
|
||||
TableModel *meanShift() const { return mean_shift_.get(); }
|
||||
void setMeanShift(TableModel *table);
|
||||
TableModel *skewness() const { return skewness_.get(); }
|
||||
void setSkewness(TableModel *table);
|
||||
TableModel *stdDev() const { return std_dev_.get(); }
|
||||
void setStdDev(TableModel *table);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<TableModel> model_;
|
||||
// Note early/late can point to the same model.
|
||||
std::array<TableModel*, EarlyLate::index_count> sigma_;
|
||||
std::unique_ptr<TableModel> std_dev_;
|
||||
std::unique_ptr<TableModel> mean_shift_;
|
||||
std::unique_ptr<TableModel> skewness_;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
class ReceiverModel
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "Delay.hh"
|
||||
#include "LibertyClass.hh"
|
||||
#include "Variables.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -52,14 +53,23 @@ public:
|
|||
virtual void gateDelay(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
bool pocv_enabled,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const = 0;
|
||||
float &gate_delay,
|
||||
float &drvr_slew) const = 0;
|
||||
// Fill in pocv parameters in gate_delay, drvr_slew.
|
||||
virtual void gateDelayPocv(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
// Return values.
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const = 0;
|
||||
virtual std::string reportGateDelay(const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap,
|
||||
bool pocv_enabled,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
int digits) const = 0;
|
||||
virtual float driveResistance(const Pvt *pvt) const = 0;
|
||||
};
|
||||
|
|
@ -74,13 +84,15 @@ public:
|
|||
float from_slew,
|
||||
float to_slew,
|
||||
float related_out_cap,
|
||||
bool pocv_enabled) const = 0;
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode) const = 0;
|
||||
virtual std::string reportCheckDelay(const Pvt *pvt,
|
||||
float from_slew,
|
||||
const char *from_slew_annotation,
|
||||
float to_slew,
|
||||
float related_out_cap,
|
||||
bool pocv_enabled,
|
||||
const MinMax *min_max,
|
||||
PocvMode pocv_mode,
|
||||
int digits) const = 0;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,8 @@ public:
|
|||
void setDigits(int digits);
|
||||
// Does not include suffix.
|
||||
int width() const;
|
||||
const char *asString(float value) const;
|
||||
const char *asString(double value) const;
|
||||
const char *asString(float value,
|
||||
std::string asString(float value) const;
|
||||
std::string asString(float value,
|
||||
int digits) const;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "PocvMode.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
enum class CrprMode { same_pin, same_transition };
|
||||
|
|
@ -72,8 +74,11 @@ public:
|
|||
// TCL variable sta_input_port_default_clock.
|
||||
bool useDefaultArrivalClock() { return use_default_arrival_clock_; }
|
||||
void setUseDefaultArrivalClock(bool enable);
|
||||
bool pocvEnabled() const { return pocv_enabled_; }
|
||||
void setPocvEnabled(bool enabled);
|
||||
bool pocvEnabled() const;
|
||||
PocvMode pocvMode() const { return pocv_mode_; }
|
||||
void setPocvMode(PocvMode mode);
|
||||
float pocvQuantile() const { return pocv_quantile_; }
|
||||
void setPocvQuantile(float quartile);
|
||||
|
||||
private:
|
||||
bool crpr_enabled_;
|
||||
|
|
@ -88,7 +93,8 @@ private:
|
|||
bool dynamic_loop_breaking_;
|
||||
bool propagate_all_clks_;
|
||||
bool use_default_arrival_clock_;
|
||||
bool pocv_enabled_;
|
||||
PocvMode pocv_mode_;
|
||||
float pocv_quantile_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -29,21 +29,21 @@
|
|||
namespace sta {
|
||||
|
||||
std::string
|
||||
cellVerilogName(const char *sta_name);
|
||||
cellVerilogName(std::string sta_name);
|
||||
std::string
|
||||
instanceVerilogName(const char *sta_name);
|
||||
instanceVerilogName(std::string sta_name);
|
||||
std::string
|
||||
netVerilogName(const char *sta_name);
|
||||
netVerilogName(std::string sta_name);
|
||||
std::string
|
||||
portVerilogName(const char *sta_name);
|
||||
portVerilogName(std::string sta_name);
|
||||
|
||||
std::string
|
||||
moduleVerilogToSta(const std::string *sta_name);
|
||||
moduleVerilogToSta(std::string sta_name);
|
||||
std::string
|
||||
instanceVerilogToSta(const std::string *sta_name);
|
||||
instanceVerilogToSta(std::string sta_name);
|
||||
std::string
|
||||
netVerilogToSta(const std::string *sta_name);
|
||||
netVerilogToSta(std::string sta_name);
|
||||
std::string
|
||||
portVerilogToSta(const std::string *sta_name);
|
||||
portVerilogToSta(std::string sta_name);
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -25,9 +25,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "Format.hh"
|
||||
#include "Report.hh"
|
||||
#include "StringUtil.hh"
|
||||
#include "NetworkClass.hh"
|
||||
|
||||
|
|
@ -59,8 +62,32 @@ class StringRegistry;
|
|||
class VerilogBindingTbl;
|
||||
class VerilogNetNameIterator;
|
||||
class VerilogNetPortRef;
|
||||
class VerilogError;
|
||||
class LibertyCell;
|
||||
class VerilogErrorCmp;
|
||||
|
||||
class VerilogError
|
||||
{
|
||||
public:
|
||||
VerilogError(int id,
|
||||
std::string_view filename,
|
||||
int line,
|
||||
std::string_view msg,
|
||||
bool warn);
|
||||
const char *msg() const { return msg_.c_str(); }
|
||||
const char *filename() const { return filename_.c_str(); }
|
||||
int id() const { return id_; }
|
||||
int line() const { return line_; }
|
||||
bool warn() const { return warn_; }
|
||||
|
||||
private:
|
||||
int id_;
|
||||
std::string filename_;
|
||||
int line_;
|
||||
std::string msg_;
|
||||
bool warn_;
|
||||
|
||||
friend class VerilogErrorCmp;
|
||||
};
|
||||
|
||||
using VerilogModuleMap = std::map<Cell*, VerilogModule*>;
|
||||
using VerilogStmtSeq = std::vector<VerilogStmt*>;
|
||||
|
|
@ -148,14 +175,24 @@ public:
|
|||
const char *filename() const { return filename_.c_str(); }
|
||||
void incrLine();
|
||||
Report *report() const { return report_; }
|
||||
template <typename... Args>
|
||||
void error(int id,
|
||||
const char *filename,
|
||||
std::string_view filename,
|
||||
int line,
|
||||
const char *fmt, ...);
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
report_->fileError(id, filename, line, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
void warn(int id,
|
||||
const char *filename,
|
||||
std::string_view filename,
|
||||
int line,
|
||||
const char *fmt, ...);
|
||||
std::string_view fmt,
|
||||
Args &&...args)
|
||||
{
|
||||
report_->fileWarn(id, filename, line, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
const std::string &zeroNetName() const { return zero_net_name_; }
|
||||
const std::string &oneNetName() const { return one_net_name_; }
|
||||
void deleteModules();
|
||||
|
|
@ -231,16 +268,26 @@ protected:
|
|||
Instance *parent,
|
||||
VerilogBindingTbl *parent_bindings,
|
||||
bool is_leaf);
|
||||
template <typename... Args>
|
||||
void linkWarn(int id,
|
||||
const char *filename,
|
||||
std::string_view filename,
|
||||
int line,
|
||||
const char *msg, ...)
|
||||
__attribute__((format (printf, 5, 6)));
|
||||
std::string_view msg,
|
||||
Args &&...args)
|
||||
{
|
||||
std::string msg_str = sta::formatRuntime(msg, std::forward<Args>(args)...);
|
||||
link_errors_.push_back(new VerilogError(id, filename, line, msg_str, true));
|
||||
}
|
||||
template <typename... Args>
|
||||
void linkError(int id,
|
||||
const char *filename,
|
||||
std::string_view filename,
|
||||
int line,
|
||||
const char *msg, ...)
|
||||
__attribute__((format (printf, 5, 6)));
|
||||
std::string_view msg,
|
||||
Args &&...args)
|
||||
{
|
||||
std::string msg_str = sta::formatRuntime(msg, std::forward<Args>(args)...);
|
||||
link_errors_.push_back(new VerilogError(id, filename, line, msg_str, false));
|
||||
}
|
||||
bool reportLinkErrors();
|
||||
bool haveLinkErrors();
|
||||
Cell *makeBlackBox(VerilogModuleInst *mod_inst,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ InternalPower::InternalPower(LibertyPort *port,
|
|||
LibertyPort *related_port,
|
||||
LibertyPort *related_pg_pin,
|
||||
const std::shared_ptr<FuncExpr> &when,
|
||||
InternalPowerModels &models) :
|
||||
const InternalPowerModels &models) :
|
||||
port_(port),
|
||||
related_port_(related_port),
|
||||
related_pg_pin_(related_pg_pin),
|
||||
|
|
@ -52,36 +52,32 @@ InternalPower::libertyCell() const
|
|||
return port_->libertyCell();
|
||||
}
|
||||
|
||||
const InternalPowerModel &
|
||||
InternalPower::model(const RiseFall *rf) const
|
||||
{
|
||||
return models_[rf->index()];
|
||||
}
|
||||
|
||||
float
|
||||
InternalPower::power(const RiseFall *rf,
|
||||
const Pvt *pvt,
|
||||
float in_slew,
|
||||
float load_cap) const
|
||||
{
|
||||
const std::shared_ptr<InternalPowerModel> &model = models_[rf->index()];
|
||||
if (model)
|
||||
return model->power(libertyCell(), pvt, in_slew, load_cap);
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const InternalPowerModel *
|
||||
InternalPower::model(const RiseFall *rf) const
|
||||
{
|
||||
const std::shared_ptr<InternalPowerModel> &m = models_[rf->index()];
|
||||
return m.get();
|
||||
const InternalPowerModel &model = models_[rf->index()];
|
||||
return model.power(libertyCell(), pvt, in_slew, load_cap);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
InternalPowerModel::InternalPowerModel(TableModel *model) :
|
||||
model_(model)
|
||||
InternalPowerModel::InternalPowerModel() :
|
||||
model_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
InternalPowerModel::~InternalPowerModel()
|
||||
InternalPowerModel::InternalPowerModel(std::shared_ptr<TableModel> model) :
|
||||
model_(model)
|
||||
{
|
||||
delete model_;
|
||||
}
|
||||
|
||||
float
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "FuncExpr.hh"
|
||||
|
|
@ -80,8 +80,7 @@ LibExprReader::makeFuncExprPort(const char *port_name)
|
|||
if (port)
|
||||
expr = FuncExpr::makePort(port);
|
||||
else
|
||||
report_->warn(1130, "%s references unknown port %s.",
|
||||
error_msg_, port_name);
|
||||
report_->warn(1130, "{} references unknown port {}.", error_msg_, port_name);
|
||||
stringDelete(port_name);
|
||||
return expr;
|
||||
}
|
||||
|
|
@ -134,7 +133,7 @@ LibExprReader::setResult(FuncExpr *result)
|
|||
void
|
||||
LibExprReader::parseError(const char *msg)
|
||||
{
|
||||
report_->error(1131, "%s %s.", error_msg_, msg);
|
||||
report_->error(1131, "{} {}.", error_msg_, msg);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -144,4 +143,4 @@ LibExprScanner::LibExprScanner(std::istringstream &stream) :
|
|||
{
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "Liberty.hh"
|
||||
|
||||
#include "ContainerHelpers.hh"
|
||||
#include "Format.hh"
|
||||
#include "Mutex.hh"
|
||||
#include "EnumNameMap.hh"
|
||||
#include "Report.hh"
|
||||
|
|
@ -775,7 +776,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1,
|
|||
port1->setScenePort(port2, ap_index);
|
||||
}
|
||||
else
|
||||
report->warn(1110, "cell %s/%s port %s not found in cell %s/%s.",
|
||||
report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.",
|
||||
cell1->library()->name(),
|
||||
cell1->name(),
|
||||
port_name,
|
||||
|
|
@ -801,7 +802,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1,
|
|||
}
|
||||
}
|
||||
else
|
||||
report->warn(1111, "cell %s/%s %s -> %s timing group %s not found in cell %s/%s.",
|
||||
report->warn(1111, "cell {}/{} {} -> {} timing group {} not found in cell {}/{}.",
|
||||
cell1->library()->name(),
|
||||
cell1->name(),
|
||||
arc_set1->from() ? arc_set1->from()->name() : "",
|
||||
|
|
@ -820,7 +821,7 @@ LibertyLibrary::checkScenes(LibertyCell *cell,
|
|||
for (const Scene *scene : scenes) {
|
||||
for (auto min_max : MinMax::range()) {
|
||||
if (!cell->checkSceneCell(scene, min_max))
|
||||
report->error(1112, "Liberty cell %s/%s for corner %s/%s not found.",
|
||||
report->error(1112, "Liberty cell {}/{} for corner {}/{} not found.",
|
||||
cell->libertyLibrary()->name(),
|
||||
cell->name(),
|
||||
scene->name().c_str(),
|
||||
|
|
@ -1262,7 +1263,7 @@ LibertyCell::makeInternalPower(LibertyPort *port,
|
|||
LibertyPort *related_port,
|
||||
LibertyPort *related_pg_pin,
|
||||
const std::shared_ptr<FuncExpr> &when,
|
||||
InternalPowerModels &models)
|
||||
const InternalPowerModels &models)
|
||||
{
|
||||
internal_powers_.emplace_back(port, related_port, related_pg_pin, when, models);
|
||||
port_internal_powers_[port].push_back(internal_powers_.size() - 1);
|
||||
|
|
@ -1703,7 +1704,7 @@ LibertyCell::makeLatchEnables(Report *report,
|
|||
TimingSense en_sense = en_func->portTimingSense(en);
|
||||
if (en_sense == TimingSense::positive_unate
|
||||
&& en_rf != RiseFall::rise())
|
||||
report->warn(1114, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function positive sense.",
|
||||
report->warn(1114, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function positive sense.",
|
||||
library_->name(),
|
||||
name(),
|
||||
en->name(),
|
||||
|
|
@ -1711,7 +1712,7 @@ LibertyCell::makeLatchEnables(Report *report,
|
|||
en_rf == RiseFall::rise()?"rising":"falling");
|
||||
else if (en_sense == TimingSense::negative_unate
|
||||
&& en_rf != RiseFall::fall())
|
||||
report->warn(1115, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with latch group enable function negative sense.",
|
||||
report->warn(1115, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with latch group enable function negative sense.",
|
||||
library_->name(),
|
||||
name(),
|
||||
en->name(),
|
||||
|
|
@ -1721,7 +1722,7 @@ LibertyCell::makeLatchEnables(Report *report,
|
|||
}
|
||||
}
|
||||
else
|
||||
report->warn(1121, "cell %s/%s no latch enable found for %s -> %s.",
|
||||
report->warn(1121, "cell {}/{} no latch enable found for {} -> {}.",
|
||||
library_->name(),
|
||||
name(),
|
||||
d->name(),
|
||||
|
|
@ -1767,7 +1768,7 @@ LibertyCell::findLatchSetup(const LibertyPort *d,
|
|||
for (TimingArc *arc : arc_set->arcs()) {
|
||||
const RiseFall *from_rf = arc->fromEdge()->asRiseFall();
|
||||
if (from_rf == en_rf) {
|
||||
report->warn(1113, "cell %s/%s %s -> %s latch enable %s_edge is inconsistent with %s -> %s setup_%s check.",
|
||||
report->warn(1113, "cell {}/{} {} -> {} latch enable {}_edge is inconsistent with {} -> {} setup_{} check.",
|
||||
library_->name(),
|
||||
name(),
|
||||
en->name(),
|
||||
|
|
@ -1824,7 +1825,7 @@ LibertyCell::makeLatchEnable(LibertyPort *d,
|
|||
latch_check_map_[setup_check] = idx;
|
||||
d->setIsLatchData(true);
|
||||
debugPrint(debug, "liberty_latch", 1,
|
||||
"latch %s -> %s | %s %s -> %s | %s %s -> %s setup",
|
||||
"latch {} -> {} | {} {} -> {} | {} {} -> {} setup",
|
||||
d->name(),
|
||||
q->name(),
|
||||
en->name(),
|
||||
|
|
@ -2904,7 +2905,7 @@ ModeDef::defineValue(const char *value,
|
|||
const char *sdf_cond)
|
||||
{
|
||||
std::string key = value;
|
||||
std::string sdf = sdf_cond ? std::string(sdf_cond) : std::string();
|
||||
std::string sdf = sdf_cond ? sdf_cond : std::string();
|
||||
auto [it, inserted] = values_.try_emplace(key, key, cond, std::move(sdf));
|
||||
return &it->second;
|
||||
}
|
||||
|
|
@ -3202,27 +3203,27 @@ ScaleFactors::report(Report *report)
|
|||
std::string line = " ";
|
||||
for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
|
||||
ScaleFactorPvt pvt = (ScaleFactorPvt) pvt_index;
|
||||
stringAppend(line, "%10s", scaleFactorPvtName(pvt));
|
||||
line += sta::format("{:>10}", scaleFactorPvtName(pvt));
|
||||
}
|
||||
report->reportLineString(line);
|
||||
report->reportLine(line);
|
||||
|
||||
for (int type_index = 0; type_index < scale_factor_type_count; type_index++) {
|
||||
ScaleFactorType type = (ScaleFactorType) type_index;
|
||||
stringPrint(line, "%10s ", scaleFactorTypeName(type));
|
||||
std::string line = sta::format("{:>10}", scaleFactorTypeName(type));
|
||||
for (int pvt_index = 0; pvt_index < scale_factor_pvt_count; pvt_index++) {
|
||||
if (scaleFactorTypeRiseFallSuffix(type)
|
||||
|| scaleFactorTypeRiseFallPrefix(type)
|
||||
|| scaleFactorTypeLowHighSuffix(type)) {
|
||||
stringAppend(line, " %.3f,%.3f",
|
||||
line += sta::format(" {:.3f},{:.3f}",
|
||||
scales_[type_index][pvt_index][RiseFall::riseIndex()],
|
||||
scales_[type_index][pvt_index][RiseFall::fallIndex()]);
|
||||
}
|
||||
else {
|
||||
stringAppend(line, " %.3f",
|
||||
line += sta::format(" {:.3f}",
|
||||
scales_[type_index][pvt_index][0]);
|
||||
}
|
||||
}
|
||||
report->reportLineString(line);
|
||||
report->reportLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -367,16 +367,13 @@ std::string to_string() { return self->to_string(); }
|
|||
const TimingRole *role() { return self->role(); }
|
||||
const char *sdf_cond() { return self->sdfCond().c_str(); }
|
||||
|
||||
const char *
|
||||
std::string
|
||||
full_name()
|
||||
{
|
||||
const char *from = self->from()->name();
|
||||
const char *to = self->to()->name();
|
||||
const char *cell_name = self->libertyCell()->name();
|
||||
return stringPrintTmp("%s %s -> %s",
|
||||
cell_name,
|
||||
from,
|
||||
to);
|
||||
return sta::format("{} {} -> {}", cell_name, from, to);
|
||||
}
|
||||
|
||||
const std::string
|
||||
|
|
|
|||
|
|
@ -102,12 +102,8 @@ LibertyBuilder::makeBusPortBit(ConcreteLibrary *library,
|
|||
const char *bus_name,
|
||||
int bit_index)
|
||||
{
|
||||
std::string bit_name;
|
||||
stringPrint(bit_name, "%s%c%d%c",
|
||||
bus_name,
|
||||
library->busBrktLeft(),
|
||||
bit_index,
|
||||
library->busBrktRight());
|
||||
std::string bit_name = std::string(bus_name) + library->busBrktLeft()
|
||||
+ std::to_string(bit_index) + library->busBrktRight();
|
||||
LibertyPort *port = makePort(cell, bit_name.c_str(), bit_index);
|
||||
bus_port->addPortBit(port);
|
||||
cell->addPortBit(port);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ sta::LibertyParse::error(const location_type &loc,
|
|||
const std::string &msg)
|
||||
{
|
||||
reader->report()->fileError(164, reader->filename().c_str(),
|
||||
loc.begin.line, "%s", msg.c_str());
|
||||
loc.begin.line, "{}", msg);
|
||||
}
|
||||
|
||||
%}
|
||||
|
|
@ -169,13 +169,13 @@ attr_value:
|
|||
/* Crafted to avoid conflicts with expr */
|
||||
volt_expr:
|
||||
FLOAT volt_op FLOAT
|
||||
{ $$ = sta::stdstrPrint("%e%c%e", $1, $2, $3); }
|
||||
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
|
||||
| string volt_op FLOAT
|
||||
{ $$ = sta::stdstrPrint("%s%c%e", $1.c_str(), $2, $3); }
|
||||
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
|
||||
| FLOAT volt_op string
|
||||
{ $$ = sta::stdstrPrint("%e%c%s", $1, $2, $3.c_str()); }
|
||||
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
|
||||
| volt_expr volt_op FLOAT
|
||||
{ $$ = sta::stdstrPrint("%s%c%e", $1.c_str(), $2, $3); }
|
||||
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
|
||||
;
|
||||
|
||||
volt_op:
|
||||
|
|
@ -192,7 +192,7 @@ volt_op:
|
|||
expr:
|
||||
expr_term1
|
||||
| expr_term1 expr_op expr
|
||||
{ $$ = sta::stdstrPrint("%s%c%s", $1.c_str(), $2, $3.c_str()); }
|
||||
{ $$ = sta::format("{}{}{}", $1.c_str(), $2, $3.c_str()); }
|
||||
;
|
||||
|
||||
expr_term:
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "LibertyParser.hh"
|
||||
|
|
@ -130,10 +130,8 @@ LibertyParser::groupBegin(const std::string type,
|
|||
LibertyAttrValueSeq *params,
|
||||
int line)
|
||||
{
|
||||
LibertyGroup *group =
|
||||
new LibertyGroup(std::move(type),
|
||||
params ? std::move(*params) : LibertyAttrValueSeq(),
|
||||
line);
|
||||
LibertyGroup *group = new LibertyGroup(
|
||||
std::move(type), params ? std::move(*params) : LibertyAttrValueSeq(), line);
|
||||
delete params;
|
||||
LibertyGroup *parent_group = group_stack_.empty() ? nullptr : group_stack_.back();
|
||||
group_visitor_->begin(group, parent_group);
|
||||
|
|
@ -145,8 +143,7 @@ LibertyParser::groupEnd()
|
|||
{
|
||||
LibertyGroup *group = this->group();
|
||||
group_stack_.pop_back();
|
||||
LibertyGroup *parent =
|
||||
group_stack_.empty() ? nullptr : group_stack_.back();
|
||||
LibertyGroup *parent = group_stack_.empty() ? nullptr : group_stack_.back();
|
||||
if (parent)
|
||||
parent->addSubgroup(group);
|
||||
group_visitor_->end(group, parent);
|
||||
|
|
@ -170,8 +167,8 @@ LibertyParser::makeSimpleAttr(const std::string name,
|
|||
const LibertyAttrValue *value,
|
||||
int line)
|
||||
{
|
||||
LibertySimpleAttr *attr = new LibertySimpleAttr(std::move(name),
|
||||
std::move(*value), line);
|
||||
LibertySimpleAttr *attr =
|
||||
new LibertySimpleAttr(std::move(name), std::move(*value), line);
|
||||
delete value;
|
||||
LibertyGroup *group = this->group();
|
||||
group->addAttr(attr);
|
||||
|
|
@ -191,9 +188,8 @@ LibertyParser::makeComplexAttr(const std::string name,
|
|||
return nullptr; // Define is not a complex attr; already added to group
|
||||
}
|
||||
else {
|
||||
LibertyComplexAttr *attr = new LibertyComplexAttr(std::move(name),
|
||||
std::move(*values),
|
||||
line);
|
||||
LibertyComplexAttr *attr =
|
||||
new LibertyComplexAttr(std::move(name), std::move(*values), line);
|
||||
delete values;
|
||||
LibertyGroup *group = this->group();
|
||||
group->addAttr(attr);
|
||||
|
|
@ -266,7 +262,7 @@ LibertyScanner::includeBegin()
|
|||
}
|
||||
else {
|
||||
report_->fileWarn(25, filename_.c_str(), yylineno,
|
||||
"cannot open include file %s.", filename.c_str());
|
||||
"cannot open include file {}.", filename);
|
||||
delete stream;
|
||||
}
|
||||
}
|
||||
|
|
@ -291,7 +287,7 @@ LibertyScanner::fileEnd()
|
|||
void
|
||||
LibertyScanner::error(const char *msg)
|
||||
{
|
||||
report_->fileError(1866, filename_.c_str(), lineno(), "%s", msg);
|
||||
report_->fileError(1866, filename_.c_str(), lineno(), "{}", msg);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
|
@ -305,10 +301,7 @@ LibertyGroup::LibertyGroup(std::string type,
|
|||
{
|
||||
}
|
||||
|
||||
LibertyGroup::~LibertyGroup()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
LibertyGroup::~LibertyGroup() { clear(); }
|
||||
|
||||
void
|
||||
LibertyGroup::clear()
|
||||
|
|
@ -327,19 +320,15 @@ LibertyGroup::clear()
|
|||
bool
|
||||
LibertyGroup::empty() const
|
||||
{
|
||||
return subgroups_.empty()
|
||||
&& simple_attr_map_.empty()
|
||||
&& complex_attr_map_.empty()
|
||||
&& define_map_.empty();
|
||||
return subgroups_.empty() && simple_attr_map_.empty() && complex_attr_map_.empty()
|
||||
&& define_map_.empty();
|
||||
}
|
||||
|
||||
bool
|
||||
LibertyGroup::oneGroupOnly() const
|
||||
{
|
||||
return subgroups_.size() == 1
|
||||
&& simple_attr_map_.empty()
|
||||
&& complex_attr_map_.empty()
|
||||
&& define_map_.empty();
|
||||
return subgroups_.size() == 1 && simple_attr_map_.empty()
|
||||
&& complex_attr_map_.empty() && define_map_.empty();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -483,7 +472,7 @@ LibertyGroup::findAttrFloat(const std::string attr_name,
|
|||
const std::string &float_str = attr_value.stringValue();
|
||||
char *end = nullptr;
|
||||
value = std::strtof(float_str.c_str(), &end);
|
||||
if (end) {
|
||||
if (end) {
|
||||
exists = true;
|
||||
return;
|
||||
}
|
||||
|
|
@ -538,10 +527,7 @@ LibertyComplexAttr::LibertyComplexAttr(std::string name,
|
|||
{
|
||||
}
|
||||
|
||||
LibertyComplexAttr::~LibertyComplexAttr()
|
||||
{
|
||||
deleteContents(values_);
|
||||
}
|
||||
LibertyComplexAttr::~LibertyComplexAttr() { deleteContents(values_); }
|
||||
|
||||
const LibertyAttrValue *
|
||||
LibertyComplexAttr::firstValue() const
|
||||
|
|
@ -585,9 +571,9 @@ LibertyAttrValue::floatValue() const
|
|||
}
|
||||
|
||||
void
|
||||
LibertyAttrValue::floatValue(// Return values.
|
||||
float &value,
|
||||
bool &valid) const
|
||||
LibertyAttrValue::floatValue( // Return values.
|
||||
float &value,
|
||||
bool &valid) const
|
||||
{
|
||||
valid = false;
|
||||
if (string_value_.empty()) {
|
||||
|
|
@ -598,8 +584,7 @@ LibertyAttrValue::floatValue(// Return values.
|
|||
// Some floats are enclosed in quotes.
|
||||
char *end;
|
||||
value = strtof(string_value_.c_str(), &end);
|
||||
if ((*end == '\0'
|
||||
|| isspace(*end))
|
||||
if ((*end == '\0' || isspace(*end))
|
||||
// strtof support INF as a valid float.
|
||||
&& string_value_ != "inf") {
|
||||
valid = true;
|
||||
|
|
@ -631,4 +616,4 @@ LibertyVariable::LibertyVariable(std::string var,
|
|||
{
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -28,6 +28,7 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
|
|
@ -44,6 +45,7 @@
|
|||
#include "LibertyParser.hh"
|
||||
#include "LibertyReader.hh"
|
||||
#include "LibertyBuilder.hh"
|
||||
#include "Report.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
|
|
@ -232,25 +234,21 @@ protected:
|
|||
const LibertyPortSeq &ports,
|
||||
const LibertyGroup *port_group);
|
||||
bool isGateTimingType(TimingType timing_type);
|
||||
TableModel *readGateTableModel(const LibertyGroup *timing_group,
|
||||
const char *table_group_name,
|
||||
const RiseFall *rf,
|
||||
TableTemplateType template_type,
|
||||
float scale,
|
||||
ScaleFactorType scale_factor_type);
|
||||
TableModel *readTableModel(const LibertyGroup *timing_group,
|
||||
const std::string &table_group_name,
|
||||
const RiseFall *rf,
|
||||
TableTemplateType template_type,
|
||||
float scale,
|
||||
ScaleFactorType scale_factor_type,
|
||||
const std::function<bool(TableModel *model)> check_axes);
|
||||
TableModelsEarlyLate
|
||||
readEarlyLateTableModels(const LibertyGroup *timing_group,
|
||||
const char *table_group_name,
|
||||
const RiseFall *rf,
|
||||
TableTemplateType template_type,
|
||||
float scale,
|
||||
ScaleFactorType scale_factor_type);
|
||||
TableModel *readCheckTableModel(const LibertyGroup *timing_group,
|
||||
const char *table_group_name,
|
||||
const RiseFall *rf,
|
||||
TableTemplateType template_type,
|
||||
float scale,
|
||||
ScaleFactorType scale_factor_type);
|
||||
ScaleFactorType scale_factor_type,
|
||||
const std::function<bool(TableModel *model)> check_axes);
|
||||
ReceiverModelPtr readReceiverCapacitance(const LibertyGroup *timing_group,
|
||||
const RiseFall *rf);
|
||||
void readReceiverCapacitance(const LibertyGroup *timing_group,
|
||||
|
|
@ -268,7 +266,9 @@ protected:
|
|||
const RiseFall *rf,
|
||||
TableTemplateType template_type,
|
||||
float scale,
|
||||
ScaleFactorType scale_factor_type);
|
||||
ScaleFactorType scale_factor_type,
|
||||
const std::function<bool(TableModel *model)> &check_axes =
|
||||
[](TableModel *) { return true; });
|
||||
TablePtr readTableModel(const LibertyGroup *table_group,
|
||||
const TableTemplate *tbl_template,
|
||||
float scale);
|
||||
|
|
@ -281,6 +281,14 @@ protected:
|
|||
void makeTableModels(LibertyCell *cell,
|
||||
const LibertyGroup *timing_group,
|
||||
TimingArcAttrsPtr timing_attrs);
|
||||
void readLvfModels(const LibertyGroup *timing_group,
|
||||
const std::string &sigma_group_name,
|
||||
const std::string &std_dev_group_name,
|
||||
const std::string &mean_shift_group_name,
|
||||
const std::string &skewness_group_name,
|
||||
const RiseFall *rf,
|
||||
TableModels *table_models,
|
||||
const std::function<bool(TableModel *model)> check_axes);
|
||||
|
||||
TableAxisPtr makeTableAxis(const LibertyGroup *table_group,
|
||||
const char *index_attr_name,
|
||||
|
|
@ -445,38 +453,65 @@ protected:
|
|||
const char *attr_name,
|
||||
const LibertyCell *cell,
|
||||
int line);
|
||||
void libWarn(int id,
|
||||
template <typename... Args>
|
||||
void warn(int id,
|
||||
const LibertyGroup *group,
|
||||
const char *fmt,
|
||||
...) const
|
||||
__attribute__((format (printf, 4, 5)));
|
||||
void libWarn(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args) const
|
||||
{
|
||||
report_->fileWarn(id, filename_, group->line(), fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
void warn(int id,
|
||||
const LibertySimpleAttr *attr,
|
||||
const char *fmt,
|
||||
...) const
|
||||
__attribute__((format (printf, 4, 5)));
|
||||
void libWarn(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args) const
|
||||
{
|
||||
report_->fileWarn(id, filename_, attr->line(), fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
void warn(int id,
|
||||
const LibertyComplexAttr *attr,
|
||||
const char *fmt,
|
||||
...) const
|
||||
__attribute__((format (printf, 4, 5)));
|
||||
void libWarn(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args) const
|
||||
{
|
||||
report_->fileWarn(id, filename_, attr->line(), fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
void warn(int id,
|
||||
int line,
|
||||
const char *fmt,
|
||||
...) const
|
||||
__attribute__((format (printf, 4, 5)));
|
||||
void libError(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args) const
|
||||
{
|
||||
report_->fileWarn(id, filename_, line, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
void error(int id,
|
||||
const LibertyGroup *group,
|
||||
const char *fmt, ...) const
|
||||
__attribute__((format (printf, 4, 5)));
|
||||
void libError(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args) const
|
||||
{
|
||||
report_->fileError(id, filename_, group->line(), fmt,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
void error(int id,
|
||||
const LibertySimpleAttr *attr,
|
||||
const char *fmt, ...) const
|
||||
__attribute__((format (printf, 4, 5)));
|
||||
void libError(int id,
|
||||
std::string_view fmt,
|
||||
Args &&...args) const
|
||||
{
|
||||
report_->fileError(id, filename_, attr->line(), fmt,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... Args>
|
||||
void error(int id,
|
||||
const LibertyComplexAttr *attr,
|
||||
const char *fmt, ...) const
|
||||
__attribute__((format (printf, 4, 5)));
|
||||
std::string_view fmt,
|
||||
Args &&...args) const
|
||||
{
|
||||
report_->fileError(id, filename_, attr->line(), fmt,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
const char *filename_;
|
||||
bool infer_latches_;
|
||||
|
|
|
|||
|
|
@ -1,32 +1,34 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "LibertyWriter.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
|
||||
#include "Format.hh"
|
||||
#include "Units.hh"
|
||||
#include "FuncExpr.hh"
|
||||
#include "PortDirection.hh"
|
||||
|
|
@ -44,7 +46,7 @@ class LibertyWriter
|
|||
public:
|
||||
LibertyWriter(const LibertyLibrary *lib,
|
||||
const char *filename,
|
||||
FILE *stream,
|
||||
std::ofstream &stream,
|
||||
Report *report);
|
||||
void writeLibrary();
|
||||
|
||||
|
|
@ -80,7 +82,7 @@ protected:
|
|||
|
||||
const LibertyLibrary *library_;
|
||||
const char *filename_;
|
||||
FILE *stream_;
|
||||
std::ofstream &stream_;
|
||||
Report *report_;
|
||||
const Unit *time_unit_;
|
||||
const Unit *cap_unit_;
|
||||
|
|
@ -91,11 +93,10 @@ writeLiberty(LibertyLibrary *lib,
|
|||
const char *filename,
|
||||
StaState *sta)
|
||||
{
|
||||
FILE *stream = fopen(filename, "w");
|
||||
if (stream) {
|
||||
std::ofstream stream(filename);
|
||||
if (stream.is_open()) {
|
||||
LibertyWriter writer(lib, filename, stream, sta->report());
|
||||
writer.writeLibrary();
|
||||
fclose(stream);
|
||||
}
|
||||
else
|
||||
throw FileNotWritable(filename);
|
||||
|
|
@ -103,7 +104,7 @@ writeLiberty(LibertyLibrary *lib,
|
|||
|
||||
LibertyWriter::LibertyWriter(const LibertyLibrary *lib,
|
||||
const char *filename,
|
||||
FILE *stream,
|
||||
std::ofstream &stream,
|
||||
Report *report) :
|
||||
library_(lib),
|
||||
filename_(filename),
|
||||
|
|
@ -118,87 +119,87 @@ void
|
|||
LibertyWriter::writeLibrary()
|
||||
{
|
||||
writeHeader();
|
||||
fprintf(stream_, "\n");
|
||||
sta::print(stream_, "\n");
|
||||
writeTableTemplates();
|
||||
writeBusDcls();
|
||||
fprintf(stream_, "\n");
|
||||
sta::print(stream_, "\n");
|
||||
writeCells();
|
||||
writeFooter();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LibertyWriter::writeHeader()
|
||||
{
|
||||
fprintf(stream_, "library (%s) {\n", library_->name());
|
||||
fprintf(stream_, " comment : \"\";\n");
|
||||
fprintf(stream_, " delay_model : table_lookup;\n");
|
||||
fprintf(stream_, " simulation : false;\n");
|
||||
sta::print(stream_, "library ({}) {{\n", library_->name());
|
||||
sta::print(stream_, " comment : \"\";\n");
|
||||
sta::print(stream_, " delay_model : table_lookup;\n");
|
||||
sta::print(stream_, " simulation : false;\n");
|
||||
const Unit *cap_unit = library_->units()->capacitanceUnit();
|
||||
fprintf(stream_, " capacitive_load_unit (1,%s);\n",
|
||||
cap_unit->scaleAbbrevSuffix().c_str());
|
||||
fprintf(stream_, " leakage_power_unit : 1pW;\n");
|
||||
sta::print(stream_, " capacitive_load_unit (1,{});\n",
|
||||
cap_unit->scaleAbbrevSuffix());
|
||||
sta::print(stream_, " leakage_power_unit : 1pW;\n");
|
||||
const Unit *current_unit = library_->units()->currentUnit();
|
||||
fprintf(stream_, " current_unit : \"1%s\";\n",
|
||||
current_unit->scaleAbbrevSuffix().c_str());
|
||||
sta::print(stream_, " current_unit : \"1{}\";\n",
|
||||
current_unit->scaleAbbrevSuffix());
|
||||
const Unit *res_unit = library_->units()->resistanceUnit();
|
||||
fprintf(stream_, " pulling_resistance_unit : \"1%s\";\n",
|
||||
res_unit->scaleAbbrevSuffix().c_str());
|
||||
sta::print(stream_, " pulling_resistance_unit : \"1{}\";\n",
|
||||
res_unit->scaleAbbrevSuffix());
|
||||
const Unit *time_unit = library_->units()->timeUnit();
|
||||
fprintf(stream_, " time_unit : \"1%s\";\n",
|
||||
time_unit->scaleAbbrevSuffix().c_str());
|
||||
sta::print(stream_, " time_unit : \"1{}\";\n",
|
||||
time_unit->scaleAbbrevSuffix());
|
||||
const Unit *volt_unit = library_->units()->voltageUnit();
|
||||
fprintf(stream_, " voltage_unit : \"1%s\";\n",
|
||||
volt_unit->scaleAbbrevSuffix().c_str());
|
||||
fprintf(stream_, " library_features(report_delay_calculation);\n");
|
||||
fprintf(stream_, "\n");
|
||||
sta::print(stream_, " voltage_unit : \"1{}\";\n",
|
||||
volt_unit->scaleAbbrevSuffix());
|
||||
sta::print(stream_, " library_features(report_delay_calculation);\n");
|
||||
sta::print(stream_, "\n");
|
||||
|
||||
fprintf(stream_, " input_threshold_pct_rise : %.0f;\n",
|
||||
library_->inputThreshold(RiseFall::rise()) * 100);
|
||||
fprintf(stream_, " input_threshold_pct_fall : %.0f;\n",
|
||||
library_->inputThreshold(RiseFall::fall()) * 100);
|
||||
fprintf(stream_, " output_threshold_pct_rise : %.0f;\n",
|
||||
library_->inputThreshold(RiseFall::rise()) * 100);
|
||||
fprintf(stream_, " output_threshold_pct_fall : %.0f;\n",
|
||||
library_->inputThreshold(RiseFall::fall()) * 100);
|
||||
fprintf(stream_, " slew_lower_threshold_pct_rise : %.0f;\n",
|
||||
library_->slewLowerThreshold(RiseFall::rise()) * 100);
|
||||
fprintf(stream_, " slew_lower_threshold_pct_fall : %.0f;\n",
|
||||
library_->slewLowerThreshold(RiseFall::fall()) * 100);
|
||||
fprintf(stream_, " slew_upper_threshold_pct_rise : %.0f;\n",
|
||||
library_->slewUpperThreshold(RiseFall::rise()) * 100);
|
||||
fprintf(stream_, " slew_upper_threshold_pct_fall : %.0f;\n",
|
||||
library_->slewUpperThreshold(RiseFall::rise()) * 100);
|
||||
fprintf(stream_, " slew_derate_from_library : %.1f;\n",
|
||||
library_->slewDerateFromLibrary());
|
||||
fprintf(stream_, "\n");
|
||||
sta::print(stream_, " input_threshold_pct_rise : {:.0f};\n",
|
||||
library_->inputThreshold(RiseFall::rise()) * 100);
|
||||
sta::print(stream_, " input_threshold_pct_fall : {:.0f};\n",
|
||||
library_->inputThreshold(RiseFall::fall()) * 100);
|
||||
sta::print(stream_, " output_threshold_pct_rise : {:.0f};\n",
|
||||
library_->inputThreshold(RiseFall::rise()) * 100);
|
||||
sta::print(stream_, " output_threshold_pct_fall : {:.0f};\n",
|
||||
library_->inputThreshold(RiseFall::fall()) * 100);
|
||||
sta::print(stream_, " slew_lower_threshold_pct_rise : {:.0f};\n",
|
||||
library_->slewLowerThreshold(RiseFall::rise()) * 100);
|
||||
sta::print(stream_, " slew_lower_threshold_pct_fall : {:.0f};\n",
|
||||
library_->slewLowerThreshold(RiseFall::fall()) * 100);
|
||||
sta::print(stream_, " slew_upper_threshold_pct_rise : {:.0f};\n",
|
||||
library_->slewUpperThreshold(RiseFall::rise()) * 100);
|
||||
sta::print(stream_, " slew_upper_threshold_pct_fall : {:.0f};\n",
|
||||
library_->slewUpperThreshold(RiseFall::rise()) * 100);
|
||||
sta::print(stream_, " slew_derate_from_library : {:.1f};\n",
|
||||
library_->slewDerateFromLibrary());
|
||||
sta::print(stream_, "\n");
|
||||
|
||||
bool exists;
|
||||
float max_fanout;
|
||||
library_->defaultFanoutLoad(max_fanout, exists);
|
||||
if (exists)
|
||||
fprintf(stream_, " default_max_fanout : %.0f;\n", max_fanout);
|
||||
sta::print(stream_, " default_max_fanout : {:.0f};\n", max_fanout);
|
||||
float max_slew;
|
||||
library_->defaultMaxSlew(max_slew, exists);
|
||||
if (exists)
|
||||
fprintf(stream_, " default_max_transition : %s;\n",
|
||||
time_unit_->asString(max_slew, 3));
|
||||
sta::print(stream_, " default_max_transition : {};\n",
|
||||
time_unit_->asString(max_slew, 3));
|
||||
float max_cap;
|
||||
library_->defaultMaxCapacitance(max_cap, exists);
|
||||
if (exists)
|
||||
fprintf(stream_, " default_max_capacitance : %s;\n",
|
||||
cap_unit_->asString(max_cap, 3));
|
||||
sta::print(stream_, " default_max_capacitance : {};\n",
|
||||
cap_unit_->asString(max_cap, 3));
|
||||
float fanout_load;
|
||||
library_->defaultFanoutLoad(fanout_load, exists);
|
||||
if (exists)
|
||||
fprintf(stream_, " default_fanout_load : %.2f;\n", fanout_load);
|
||||
fprintf(stream_, "\n");
|
||||
sta::print(stream_, " default_fanout_load : {:.2f};\n", fanout_load);
|
||||
sta::print(stream_, "\n");
|
||||
|
||||
fprintf(stream_, " nom_process : %.1f;\n",
|
||||
library_->nominalProcess());
|
||||
fprintf(stream_, " nom_temperature : %.1f;\n",
|
||||
library_->nominalTemperature());
|
||||
fprintf(stream_, " nom_voltage : %.2f;\n",
|
||||
library_->nominalVoltage());
|
||||
sta::print(stream_, " nom_process : {:.1f};\n",
|
||||
library_->nominalProcess());
|
||||
sta::print(stream_, " nom_temperature : {:.1f};\n",
|
||||
library_->nominalTemperature());
|
||||
sta::print(stream_, " nom_voltage : {:.2f};\n",
|
||||
library_->nominalVoltage());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -216,22 +217,22 @@ LibertyWriter::writeTableTemplate(const TableTemplate *tbl_template)
|
|||
const TableAxis *axis3 = tbl_template->axis3();
|
||||
// skip scalar templates
|
||||
if (axis1) {
|
||||
fprintf(stream_, " lu_table_template(%s) {\n", tbl_template->name().c_str());
|
||||
fprintf(stream_, " variable_1 : %s;\n",
|
||||
tableVariableString(axis1->variable()));
|
||||
sta::print(stream_, " lu_table_template({}) {{\n", tbl_template->name());
|
||||
sta::print(stream_, " variable_1 : {};\n",
|
||||
tableVariableString(axis1->variable()));
|
||||
if (axis2)
|
||||
fprintf(stream_, " variable_2 : %s;\n",
|
||||
tableVariableString(axis2->variable()));
|
||||
sta::print(stream_, " variable_2 : {};\n",
|
||||
tableVariableString(axis2->variable()));
|
||||
if (axis3)
|
||||
fprintf(stream_, " variable_3 : %s;\n",
|
||||
tableVariableString(axis3->variable()));
|
||||
sta::print(stream_, " variable_3 : {};\n",
|
||||
tableVariableString(axis3->variable()));
|
||||
if (axis1 && !axis1->values().empty())
|
||||
writeTableAxis4(axis1, 1);
|
||||
if (axis2 && !axis2->values().empty())
|
||||
writeTableAxis4(axis2, 2);
|
||||
if (axis3 && !axis3->values().empty())
|
||||
writeTableAxis4(axis3, 3);
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " }}\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -240,16 +241,16 @@ void
|
|||
LibertyWriter::writeTableAxis4(const TableAxis *axis,
|
||||
int index)
|
||||
{
|
||||
fprintf(stream_, " index_%d(\"", index);
|
||||
sta::print(stream_, " index_{}(\"", index);
|
||||
const Unit *unit = tableVariableUnit(axis->variable(), library_->units());
|
||||
bool first = true;
|
||||
for (size_t i = 0; i < axis->size(); i++) {
|
||||
if (!first)
|
||||
fprintf(stream_, ", ");
|
||||
fprintf(stream_, "%s", unit->asString(axis->axisValue(i), 5));
|
||||
sta::print(stream_, ", ");
|
||||
sta::print(stream_, "{}", unit->asString(axis->axisValue(i), 5));
|
||||
first = false;
|
||||
}
|
||||
fprintf(stream_, "\");\n");
|
||||
sta::print(stream_, "\");\n");
|
||||
}
|
||||
|
||||
// indent 10
|
||||
|
|
@ -257,7 +258,7 @@ void
|
|||
LibertyWriter::writeTableAxis10(const TableAxis *axis,
|
||||
int index)
|
||||
{
|
||||
fprintf(stream_, " ");
|
||||
sta::print(stream_, " ");
|
||||
writeTableAxis4(axis, index);
|
||||
}
|
||||
|
||||
|
|
@ -266,13 +267,13 @@ LibertyWriter::writeBusDcls()
|
|||
{
|
||||
BusDclSeq dcls = library_->busDcls();
|
||||
for (BusDcl *dcl : dcls) {
|
||||
fprintf(stream_, " type (\"%s\") {\n", dcl->name().c_str());
|
||||
fprintf(stream_, " base_type : array;\n");
|
||||
fprintf(stream_, " data_type : bit;\n");
|
||||
fprintf(stream_, " bit_width : %d;\n", std::abs(dcl->from() - dcl->to() + 1));
|
||||
fprintf(stream_, " bit_from : %d;\n", dcl->from());
|
||||
fprintf(stream_, " bit_to : %d;\n", dcl->to());
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " type (\"{}\") {{\n", dcl->name());
|
||||
sta::print(stream_, " base_type : array;\n");
|
||||
sta::print(stream_, " data_type : bit;\n");
|
||||
sta::print(stream_, " bit_width : {};\n", std::abs(dcl->from() - dcl->to() + 1));
|
||||
sta::print(stream_, " bit_from : {};\n", dcl->from());
|
||||
sta::print(stream_, " bit_to : {};\n", dcl->to());
|
||||
sta::print(stream_, " }}\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,21 +290,20 @@ LibertyWriter::writeCells()
|
|||
void
|
||||
LibertyWriter::writeCell(const LibertyCell *cell)
|
||||
{
|
||||
fprintf(stream_, " cell (\"%s\") {\n", cell->name());
|
||||
sta::print(stream_, " cell (\"{}\") {{\n", cell->name());
|
||||
float area = cell->area();
|
||||
if (area > 0.0)
|
||||
fprintf(stream_, " area : %.3f \n", area);
|
||||
sta::print(stream_, " area : {:.3f} \n", area);
|
||||
if (cell->isMacro())
|
||||
fprintf(stream_, " is_macro_cell : true;\n");
|
||||
sta::print(stream_, " is_macro_cell : true;\n");
|
||||
if (cell->interfaceTiming())
|
||||
fprintf(stream_, " interface_timing : true;\n");
|
||||
sta::print(stream_, " interface_timing : true;\n");
|
||||
const char *footprint = cell->footprint();
|
||||
if (footprint)
|
||||
fprintf(stream_, " cell_footprint : \"%s\";\n", footprint);
|
||||
sta::print(stream_, " cell_footprint : \"{}\";\n", footprint);
|
||||
const char *user_function_class = cell->userFunctionClass();
|
||||
if (user_function_class)
|
||||
fprintf(stream_, " user_function_class : \"%s\";\n",
|
||||
user_function_class);
|
||||
sta::print(stream_, " user_function_class : \"{}\";\n", user_function_class);
|
||||
|
||||
LibertyCellPortIterator port_iter(cell);
|
||||
while (port_iter.hasNext()) {
|
||||
|
|
@ -314,24 +314,23 @@ LibertyWriter::writeCell(const LibertyCell *cell)
|
|||
else if (port->isBus())
|
||||
writeBusPort(port);
|
||||
else if (port->isBundle())
|
||||
report_->error(1340, "%s/%s bundled ports not supported.",
|
||||
library_->name(),
|
||||
report_->error(1340, "{}/{} bundled ports not supported.", library_->name(),
|
||||
cell->name());
|
||||
else
|
||||
writePort(port);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stream_, " }\n");
|
||||
fprintf(stream_, "\n");
|
||||
sta::print(stream_, " }}\n");
|
||||
sta::print(stream_, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
LibertyWriter::writeBusPort(const LibertyPort *port)
|
||||
{
|
||||
fprintf(stream_, " bus(\"%s\") {\n", port->name());
|
||||
sta::print(stream_, " bus(\"{}\") {{\n", port->name());
|
||||
if (port->busDcl())
|
||||
fprintf(stream_, " bus_type : %s;\n", port->busDcl()->name().c_str());
|
||||
sta::print(stream_, " bus_type : {};\n", port->busDcl()->name());
|
||||
writePortAttrs(port);
|
||||
|
||||
LibertyPortMemberIterator member_iter(port);
|
||||
|
|
@ -339,56 +338,53 @@ LibertyWriter::writeBusPort(const LibertyPort *port)
|
|||
LibertyPort *member = member_iter.next();
|
||||
writePort(member);
|
||||
}
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " }}\n");
|
||||
}
|
||||
|
||||
void
|
||||
LibertyWriter::writePort(const LibertyPort *port)
|
||||
{
|
||||
fprintf(stream_, " pin(\"%s\") {\n", port->name());
|
||||
sta::print(stream_, " pin(\"{}\") {{\n", port->name());
|
||||
writePortAttrs(port);
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " }}\n");
|
||||
}
|
||||
|
||||
void
|
||||
LibertyWriter::writePortAttrs(const LibertyPort *port)
|
||||
{
|
||||
fprintf(stream_, " direction : %s;\n" , asString(port->direction()));
|
||||
sta::print(stream_, " direction : {};\n", asString(port->direction()));
|
||||
auto func = port->function();
|
||||
if (func
|
||||
// cannot ref internal ports until sequentials are written
|
||||
&& !(func->port()
|
||||
&& func->port()->direction()->isInternal()))
|
||||
fprintf(stream_, " function : \"%s\";\n", func->to_string().c_str());
|
||||
&& !(func->port() && func->port()->direction()->isInternal()))
|
||||
sta::print(stream_, " function : \"{}\";\n", func->to_string());
|
||||
auto tristate_enable = port->tristateEnable();
|
||||
if (tristate_enable) {
|
||||
if (tristate_enable->op() == FuncExpr::Op::not_) {
|
||||
FuncExpr *three_state = tristate_enable->left();
|
||||
fprintf(stream_, " three_state : \"%s\";\n",
|
||||
three_state->to_string().c_str());
|
||||
sta::print(stream_, " three_state : \"{}\";\n",
|
||||
three_state->to_string());
|
||||
}
|
||||
else {
|
||||
FuncExpr *three_state = tristate_enable->copy()->invert();
|
||||
fprintf(stream_, " three_state : \"%s\";\n",
|
||||
three_state->to_string().c_str());
|
||||
sta::print(stream_, " three_state : \"{}\";\n",
|
||||
three_state->to_string());
|
||||
delete three_state;
|
||||
}
|
||||
}
|
||||
if (port->isClock())
|
||||
fprintf(stream_, " clock : true;\n");
|
||||
fprintf(stream_, " capacitance : %s;\n",
|
||||
cap_unit_->asString(port->capacitance(), 4));
|
||||
|
||||
sta::print(stream_, " clock : true;\n");
|
||||
sta::print(stream_, " capacitance : {};\n",
|
||||
cap_unit_->asString(port->capacitance(), 4));
|
||||
|
||||
float limit;
|
||||
bool exists;
|
||||
port->slewLimit(MinMax::max(), limit, exists);
|
||||
if (exists)
|
||||
fprintf(stream_, " max_transition : %s;\n",
|
||||
time_unit_->asString(limit, 3));
|
||||
sta::print(stream_, " max_transition : {};\n", time_unit_->asString(limit, 3));
|
||||
port->capacitanceLimit(MinMax::max(), limit, exists);
|
||||
if (exists)
|
||||
fprintf(stream_, " max_capacitance : %s;\n",
|
||||
cap_unit_->asString(limit, 3));
|
||||
sta::print(stream_, " max_capacitance : {};\n", cap_unit_->asString(limit, 3));
|
||||
|
||||
for (TimingArcSet *arc_set : port->libertyCell()->timingArcSetsTo(port)) {
|
||||
if (!isAutoWidthArc(port, arc_set))
|
||||
|
|
@ -399,10 +395,10 @@ LibertyWriter::writePortAttrs(const LibertyPort *port)
|
|||
void
|
||||
LibertyWriter::writePwrGndPort(const LibertyPort *port)
|
||||
{
|
||||
fprintf(stream_, " pg_pin(\"%s\") {\n", port->name());
|
||||
fprintf(stream_, " pg_type : \"%s\";\n", pwrGndTypeName(port->pwrGndType()));
|
||||
fprintf(stream_, " voltage_name : \"%s\";\n", port->voltageName());
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " pg_pin(\"{}\") {{\n", port->name());
|
||||
sta::print(stream_, " pg_type : \"{}\";\n", pwrGndTypeName(port->pwrGndType()));
|
||||
sta::print(stream_, " voltage_name : \"{}\";\n", port->voltageName());
|
||||
sta::print(stream_, " }}\n");
|
||||
}
|
||||
|
||||
// Check if arc is added for port min_pulse_width_high/low attribute.
|
||||
|
|
@ -423,30 +419,27 @@ LibertyWriter::isAutoWidthArc(const LibertyPort *port,
|
|||
void
|
||||
LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
|
||||
{
|
||||
fprintf(stream_, " timing() {\n");
|
||||
sta::print(stream_, " timing() {{\n");
|
||||
if (arc_set->from())
|
||||
fprintf(stream_, " related_pin : \"%s\";\n", arc_set->from()->name());
|
||||
sta::print(stream_, " related_pin : \"{}\";\n", arc_set->from()->name());
|
||||
TimingSense sense = arc_set->sense();
|
||||
if (sense != TimingSense::unknown
|
||||
&& sense != TimingSense::non_unate)
|
||||
fprintf(stream_, " timing_sense : %s;\n",
|
||||
to_string(sense));
|
||||
if (sense != TimingSense::unknown && sense != TimingSense::non_unate)
|
||||
sta::print(stream_, " timing_sense : {};\n", to_string(sense));
|
||||
const char *timing_type = timingTypeString(arc_set);
|
||||
if (timing_type)
|
||||
fprintf(stream_, " timing_type : %s;\n", timing_type);
|
||||
sta::print(stream_, " timing_type : {};\n", timing_type);
|
||||
|
||||
for (const RiseFall *rf : RiseFall::range()) {
|
||||
TimingArc *arc = arc_set->arcTo(rf);
|
||||
if (arc) {
|
||||
// Min pulse width arcs are wrt to the leading edge of the pulse.
|
||||
const RiseFall *model_rf = (arc_set->role() == TimingRole::width())
|
||||
? rf->opposite()
|
||||
: rf;
|
||||
const RiseFall *model_rf =
|
||||
(arc_set->role() == TimingRole::width()) ? rf->opposite() : rf;
|
||||
writeTimingModels(arc, model_rf);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " }}\n");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -454,53 +447,53 @@ LibertyWriter::writeTimingModels(const TimingArc *arc,
|
|||
const RiseFall *rf)
|
||||
{
|
||||
TimingModel *model = arc->model();
|
||||
const GateTableModel *gate_model = dynamic_cast<GateTableModel*>(model);
|
||||
const CheckTableModel *check_model = dynamic_cast<CheckTableModel*>(model);
|
||||
const GateTableModel *gate_model = dynamic_cast<GateTableModel *>(model);
|
||||
const CheckTableModel *check_model = dynamic_cast<CheckTableModel *>(model);
|
||||
if (gate_model) {
|
||||
const TableModel *delay_model = gate_model->delayModel();
|
||||
const std::string &template_name = delay_model->tblTemplate()->name();
|
||||
fprintf(stream_, " cell_%s(%s) {\n", rf->name(), template_name.c_str());
|
||||
sta::print(stream_, " cell_{}({}) {{\n", rf->name(), template_name);
|
||||
writeTableModel(delay_model);
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " }}\n");
|
||||
|
||||
const TableModel *slew_model = gate_model->slewModel();
|
||||
if (slew_model) {
|
||||
const std::string &slew_template_name = slew_model->tblTemplate()->name();
|
||||
fprintf(stream_, " %s_transition(%s) {\n", rf->name(), slew_template_name.c_str());
|
||||
sta::print(stream_, " {}_transition({}) {{\n", rf->name(),
|
||||
slew_template_name);
|
||||
writeTableModel(slew_model);
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " }}\n");
|
||||
}
|
||||
}
|
||||
else if (check_model) {
|
||||
const TableModel *model = check_model->model();
|
||||
const TableModel *model = check_model->checkModel();
|
||||
const std::string &template_name = model->tblTemplate()->name();
|
||||
fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name.c_str());
|
||||
sta::print(stream_, " {}_constraint({}) {{\n", rf->name(),
|
||||
template_name);
|
||||
writeTableModel(model);
|
||||
fprintf(stream_, " }\n");
|
||||
sta::print(stream_, " }}\n");
|
||||
}
|
||||
else
|
||||
report_->error(1341, "%s/%s/%s timing model not supported.",
|
||||
library_->name(),
|
||||
arc->from()->libertyCell()->name(),
|
||||
arc->from()->name());
|
||||
report_->error(1341, "{}/{}/{} timing model not supported.", library_->name(),
|
||||
arc->from()->libertyCell()->name(), arc->from()->name());
|
||||
}
|
||||
|
||||
void
|
||||
LibertyWriter::writeTableModel(const TableModel *model)
|
||||
{
|
||||
switch (model->order()) {
|
||||
case 0:
|
||||
writeTableModel0(model);
|
||||
break;
|
||||
case 1:
|
||||
writeTableModel1(model);
|
||||
break;
|
||||
case 2:
|
||||
writeTableModel2(model);
|
||||
break;
|
||||
case 3:
|
||||
report_->error(1342, "3 axis table models not supported.");
|
||||
break;
|
||||
case 0:
|
||||
writeTableModel0(model);
|
||||
break;
|
||||
case 1:
|
||||
writeTableModel1(model);
|
||||
break;
|
||||
case 2:
|
||||
writeTableModel2(model);
|
||||
break;
|
||||
case 3:
|
||||
report_->error(1342, "3 axis table models not supported.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -508,24 +501,23 @@ void
|
|||
LibertyWriter::writeTableModel0(const TableModel *model)
|
||||
{
|
||||
float value = model->value(0, 0, 0);
|
||||
fprintf(stream_, " values(\"%s\");\n",
|
||||
time_unit_->asString(value, 5));
|
||||
sta::print(stream_, " values(\"{}\");\n", time_unit_->asString(value, 5));
|
||||
}
|
||||
|
||||
void
|
||||
LibertyWriter::writeTableModel1(const TableModel *model)
|
||||
{
|
||||
writeTableAxis10(model->axis1(), 1);
|
||||
fprintf(stream_, " values(\"");
|
||||
sta::print(stream_, " values(\"");
|
||||
bool first_col = true;
|
||||
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
|
||||
float value = model->value(index1, 0, 0);
|
||||
if (!first_col)
|
||||
fprintf(stream_, ",");
|
||||
fprintf(stream_, "%s", time_unit_->asString(value, 5));
|
||||
sta::print(stream_, ",");
|
||||
sta::print(stream_, "{}", time_unit_->asString(value, 5));
|
||||
first_col = false;
|
||||
}
|
||||
fprintf(stream_, "\");\n");
|
||||
sta::print(stream_, "\");\n");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -533,31 +525,31 @@ LibertyWriter::writeTableModel2(const TableModel *model)
|
|||
{
|
||||
writeTableAxis10(model->axis1(), 1);
|
||||
writeTableAxis10(model->axis2(), 2);
|
||||
fprintf(stream_, " values(\"");
|
||||
sta::print(stream_, " values(\"");
|
||||
bool first_row = true;
|
||||
for (size_t index1 = 0; index1 < model->axis1()->size(); index1++) {
|
||||
if (!first_row) {
|
||||
fprintf(stream_, "\\\n");
|
||||
fprintf(stream_, " \"");
|
||||
sta::print(stream_, "\\\n");
|
||||
sta::print(stream_, " \"");
|
||||
}
|
||||
bool first_col = true;
|
||||
for (size_t index2 = 0; index2 < model->axis2()->size(); index2++) {
|
||||
float value = model->value(index1, index2, 0);
|
||||
if (!first_col)
|
||||
fprintf(stream_, ",");
|
||||
fprintf(stream_, "%s", time_unit_->asString(value, 5));
|
||||
sta::print(stream_, ",");
|
||||
sta::print(stream_, "{}", time_unit_->asString(value, 5));
|
||||
first_col = false;
|
||||
}
|
||||
fprintf(stream_, "\"");
|
||||
sta::print(stream_, "\"");
|
||||
first_row = false;
|
||||
}
|
||||
fprintf(stream_, ");\n");
|
||||
sta::print(stream_, ");\n");
|
||||
}
|
||||
|
||||
void
|
||||
LibertyWriter::writeFooter()
|
||||
{
|
||||
fprintf(stream_, "}\n");
|
||||
sta::print(stream_, "}}\n");
|
||||
}
|
||||
|
||||
const char *
|
||||
|
|
@ -571,15 +563,13 @@ LibertyWriter::asString(const PortDirection *dir)
|
|||
{
|
||||
if (dir == PortDirection::input())
|
||||
return "input";
|
||||
else if (dir == PortDirection::output()
|
||||
|| (dir == PortDirection::tristate()))
|
||||
else if (dir == PortDirection::output() || (dir == PortDirection::tristate()))
|
||||
return "output";
|
||||
else if (dir == PortDirection::internal())
|
||||
return "internal";
|
||||
else if (dir == PortDirection::bidirect())
|
||||
return "inout";
|
||||
else if (dir == PortDirection::ground()
|
||||
|| dir == PortDirection::power())
|
||||
else if (dir == PortDirection::ground() || dir == PortDirection::power())
|
||||
return "input";
|
||||
return "unknown";
|
||||
}
|
||||
|
|
@ -594,8 +584,7 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
|
|||
return "three_state_disable";
|
||||
else if (role == TimingRole::tristateEnable())
|
||||
return "three_state_enable";
|
||||
else if (role == TimingRole::regClkToQ()
|
||||
|| role == TimingRole::latchEnToQ()) {
|
||||
else if (role == TimingRole::regClkToQ() || role == TimingRole::latchEnToQ()) {
|
||||
const TimingArc *arc = arc_set->arcs()[0];
|
||||
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
|
||||
return "rising_edge";
|
||||
|
|
@ -611,16 +600,14 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
|
|||
else
|
||||
return "clear";
|
||||
}
|
||||
else if (role == TimingRole::setup()
|
||||
|| role == TimingRole::recovery()) {
|
||||
else if (role == TimingRole::setup() || role == TimingRole::recovery()) {
|
||||
const TimingArc *arc = arc_set->arcs()[0];
|
||||
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
|
||||
return "setup_rising";
|
||||
else
|
||||
return "setup_falling";
|
||||
}
|
||||
else if (role == TimingRole::hold()
|
||||
|| role == TimingRole::removal()) {
|
||||
else if (role == TimingRole::hold() || role == TimingRole::removal()) {
|
||||
const TimingArc *arc = arc_set->arcs()[0];
|
||||
if (arc->fromEdge()->asRiseFall() == RiseFall::rise())
|
||||
return "hold_rising";
|
||||
|
|
@ -648,13 +635,11 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
|
|||
else if (role == TimingRole::width())
|
||||
return "min_pulse_width";
|
||||
else {
|
||||
report_->error(1343, "%s/%s/%s timing arc type %s not supported.",
|
||||
library_->name(),
|
||||
arc_set->to()->libertyCell()->name(),
|
||||
arc_set->to()->name(),
|
||||
role->to_string().c_str());
|
||||
report_->error(1343, "{}/{}/{} timing arc type {} not supported.",
|
||||
library_->name(), arc_set->to()->libertyCell()->name(),
|
||||
arc_set->to()->name(), role->to_string());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -42,20 +42,32 @@ void
|
|||
GateLinearModel::gateDelay(const Pvt *,
|
||||
float,
|
||||
float load_cap,
|
||||
bool,
|
||||
// return values
|
||||
ArcDelay &gate_delay,
|
||||
Slew &drvr_slew) const
|
||||
float &gate_delay,
|
||||
float &drvr_slew) const
|
||||
{
|
||||
gate_delay = intrinsic_ + resistance_ * load_cap;
|
||||
drvr_slew = 0.0;
|
||||
}
|
||||
|
||||
void
|
||||
GateLinearModel::gateDelayPocv(const Pvt *,
|
||||
float,
|
||||
float,
|
||||
const MinMax *,
|
||||
PocvMode,
|
||||
// return values
|
||||
ArcDelay &,
|
||||
Slew &) const
|
||||
{
|
||||
}
|
||||
|
||||
std::string
|
||||
GateLinearModel::reportGateDelay(const Pvt *,
|
||||
float,
|
||||
float load_cap,
|
||||
bool,
|
||||
const MinMax *,
|
||||
PocvMode,
|
||||
int digits) const
|
||||
{
|
||||
const LibertyLibrary *library = cell_->libertyLibrary();
|
||||
|
|
@ -98,7 +110,8 @@ CheckLinearModel::checkDelay(const Pvt *,
|
|||
float,
|
||||
float,
|
||||
float,
|
||||
bool) const
|
||||
const MinMax *,
|
||||
PocvMode) const
|
||||
{
|
||||
return intrinsic_;
|
||||
}
|
||||
|
|
@ -109,7 +122,8 @@ CheckLinearModel::reportCheckDelay(const Pvt *,
|
|||
const char *,
|
||||
float,
|
||||
float,
|
||||
bool,
|
||||
const MinMax *,
|
||||
PocvMode,
|
||||
int digits) const
|
||||
{
|
||||
const LibertyLibrary *library = cell_->libertyLibrary();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -152,9 +152,8 @@ TimingArc::intrinsicDelay() const
|
|||
{
|
||||
GateTimingModel *model = dynamic_cast<GateTimingModel*>(model_);
|
||||
if (model) {
|
||||
ArcDelay arc_delay;
|
||||
Slew slew;
|
||||
model->gateDelay(nullptr, 0.0, 0.0, false, arc_delay, slew);
|
||||
float arc_delay, slew;
|
||||
model->gateDelay(nullptr, 0.0, 0.0, arc_delay, slew);
|
||||
return arc_delay;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <cmath> // abs
|
||||
|
||||
#include "Format.hh"
|
||||
#include "StringUtil.hh"
|
||||
#include "MinMax.hh" // INF
|
||||
#include "Fuzzy.hh"
|
||||
|
|
@ -127,7 +128,7 @@ Unit::scaleString() const
|
|||
else if (fuzzyEqual(scale_, 1E-15))
|
||||
return "1f";
|
||||
else
|
||||
return stdstrPrint("%.1e", scale_);
|
||||
return sta::format("{:.1e}", scale_);
|
||||
}
|
||||
|
||||
std::string
|
||||
|
|
@ -155,19 +156,13 @@ Unit::width() const
|
|||
return digits_ + 2;
|
||||
}
|
||||
|
||||
const char *
|
||||
std::string
|
||||
Unit::asString(float value) const
|
||||
{
|
||||
return asString(value, digits_);
|
||||
}
|
||||
|
||||
const char *
|
||||
Unit::asString(double value) const
|
||||
{
|
||||
return asString(static_cast<float>(value), digits_);
|
||||
}
|
||||
|
||||
const char *
|
||||
std::string
|
||||
Unit::asString(float value,
|
||||
int digits) const
|
||||
{
|
||||
|
|
@ -179,7 +174,7 @@ Unit::asString(float value,
|
|||
// prevent "-0.00" on slowaris
|
||||
if (std::abs(scaled_value) < 1E-6)
|
||||
scaled_value = 0.0;
|
||||
return stringPrintTmp("%.*f", digits, scaled_value);
|
||||
return sta::formatRuntime("{:.{}f}", scaled_value, digits);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -222,12 +222,10 @@ ConcreteCell::makeBusPortBit(ConcretePort *bus_port,
|
|||
const char *bus_name,
|
||||
int bit_index)
|
||||
{
|
||||
std::string bit_name;
|
||||
stringPrint(bit_name, "%s%c%d%c",
|
||||
bus_name,
|
||||
library_->busBrktLeft(),
|
||||
bit_index,
|
||||
library_->busBrktRight());
|
||||
std::string bit_name = std::string(bus_name)
|
||||
+ library_->busBrktLeft()
|
||||
+ std::to_string(bit_index)
|
||||
+ library_->busBrktRight();
|
||||
ConcretePort *port = makePort(bit_name.c_str(), bit_index);
|
||||
bus_port->addPortBit(port);
|
||||
addPortBit(port);
|
||||
|
|
@ -465,12 +463,13 @@ ConcretePort::busName() const
|
|||
{
|
||||
if (is_bus_) {
|
||||
ConcreteLibrary *lib = cell_->library();
|
||||
return stringPrintTmp("%s%c%d:%d%c",
|
||||
name(),
|
||||
lib->busBrktLeft(),
|
||||
from_index_,
|
||||
to_index_,
|
||||
lib->busBrktRight());
|
||||
std::string bus_name = sta::format("{}{}{}:{}{}",
|
||||
name(),
|
||||
lib->busBrktLeft(),
|
||||
from_index_,
|
||||
to_index_,
|
||||
lib->busBrktRight());
|
||||
return makeTmpString(bus_name);
|
||||
}
|
||||
else
|
||||
return name();
|
||||
|
|
|
|||
|
|
@ -2003,7 +2003,7 @@ ConcreteNetwork::linkNetwork(const char *top_cell_name,
|
|||
return top_instance_ != nullptr;
|
||||
}
|
||||
else {
|
||||
report->error(1000, "cell type %s can not be linked.", top_cell_name);
|
||||
report->error(1000, "cell type {} can not be linked.", top_cell_name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -293,15 +293,16 @@ HpinDrvrLoad::~HpinDrvrLoad()
|
|||
void
|
||||
HpinDrvrLoad::report(const Network *network)
|
||||
{
|
||||
printf("%s -> %s: ",
|
||||
drvr_ ? network->pathName(drvr_) : "-",
|
||||
load_ ? network->pathName(load_) : "-");
|
||||
Report *report = network->report();
|
||||
std::string line = sta::format("{} -> {}: ",
|
||||
drvr_ ? network->pathName(drvr_) : "-",
|
||||
load_ ? network->pathName(load_) : "-");
|
||||
for (const Pin *pin : *hpins_from_drvr_)
|
||||
printf("%s ", network->pathName(pin));
|
||||
printf("* ");
|
||||
line += sta::format("{} ", network->pathName(pin));
|
||||
line += "* ";
|
||||
for (const Pin *pin : *hpins_to_load_)
|
||||
printf("%s ", network->pathName(pin));
|
||||
printf("\n");
|
||||
line += sta::format("{} ", network->pathName(pin));
|
||||
report->report(line);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -262,24 +262,15 @@ Network::pathName(const Instance *instance) const
|
|||
{
|
||||
InstanceSeq inst_path;
|
||||
path(instance, inst_path);
|
||||
size_t name_length = 0;
|
||||
for (const Instance *inst : inst_path)
|
||||
name_length += strlen(name(inst)) + 1;
|
||||
char *path_name = makeTmpString(name_length + 1);
|
||||
char *path_ptr = path_name;
|
||||
// Top instance has null string name, so terminate the string here.
|
||||
*path_name = '\0';
|
||||
std::string path_name;
|
||||
while (inst_path.size()) {
|
||||
const Instance *inst = inst_path.back();
|
||||
const char *inst_name = name(inst);
|
||||
strcpy(path_ptr, inst_name);
|
||||
path_ptr += strlen(inst_name);
|
||||
path_name += name(inst);
|
||||
inst_path.pop_back();
|
||||
if (inst_path.size())
|
||||
*path_ptr++ = pathDivider();
|
||||
*path_ptr = '\0';
|
||||
if (!inst_path.empty())
|
||||
path_name += pathDivider();
|
||||
}
|
||||
return path_name;
|
||||
return makeTmpString(path_name);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -376,18 +367,10 @@ Network::pathName(const Pin *pin) const
|
|||
{
|
||||
const Instance *inst = instance(pin);
|
||||
if (inst && inst != topInstance()) {
|
||||
const char *inst_name = pathName(inst);
|
||||
size_t inst_name_length = strlen(inst_name);
|
||||
const char *port_name = portName(pin);
|
||||
size_t port_name_length = strlen(port_name);
|
||||
size_t path_name_length = inst_name_length + port_name_length + 2;
|
||||
char *path_name = makeTmpString(path_name_length);
|
||||
char *path_ptr = path_name;
|
||||
strcpy(path_ptr, inst_name);
|
||||
path_ptr += inst_name_length;
|
||||
*path_ptr++ = pathDivider();
|
||||
strcpy(path_ptr, port_name);
|
||||
return path_name;
|
||||
std::string path_name = pathName(inst);
|
||||
path_name += pathDivider();
|
||||
path_name += portName(pin);
|
||||
return makeTmpString(path_name);
|
||||
}
|
||||
else
|
||||
return portName(pin);
|
||||
|
|
@ -464,18 +447,10 @@ Network::pathName(const Net *net) const
|
|||
{
|
||||
const Instance *inst = instance(net);
|
||||
if (inst && inst != topInstance()) {
|
||||
const char *inst_name = pathName(inst);
|
||||
size_t inst_name_length = strlen(inst_name);
|
||||
const char *net_name = name(net);
|
||||
size_t net_name_length = strlen(net_name);
|
||||
size_t path_name_length = inst_name_length + net_name_length + 2;
|
||||
char *path_name = makeTmpString(path_name_length);
|
||||
char *path_ptr = path_name;
|
||||
strcpy(path_ptr, inst_name);
|
||||
path_ptr += inst_name_length;
|
||||
*path_ptr++ = pathDivider();
|
||||
strcpy(path_ptr, net_name);
|
||||
return path_name;
|
||||
std::string path_name = pathName(inst);
|
||||
path_name += pathDivider();
|
||||
path_name += name(net);
|
||||
return makeTmpString(path_name);
|
||||
}
|
||||
else
|
||||
return name(net);
|
||||
|
|
|
|||
|
|
@ -511,7 +511,7 @@ net_pins(Net *net)
|
|||
return pins;
|
||||
}
|
||||
|
||||
const char *
|
||||
std::string
|
||||
pin_location(const Pin *pin)
|
||||
{
|
||||
Network *network = Sta::sta()->ensureLinked();
|
||||
|
|
@ -520,12 +520,12 @@ pin_location(const Pin *pin)
|
|||
network->location(pin, x, y, exists);
|
||||
// return x/y as tcl list
|
||||
if (exists)
|
||||
return sta::stringPrintTmp("%f %f", x, y);
|
||||
return sta::format("{} {}", x, y);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *
|
||||
std::string
|
||||
port_location(const Port *port)
|
||||
{
|
||||
Network *network = Sta::sta()->ensureLinked();
|
||||
|
|
|
|||
|
|
@ -24,35 +24,34 @@
|
|||
|
||||
#include "ParseBus.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "StringUtil.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
bool
|
||||
isBusName(const char *name,
|
||||
isBusName(std::string_view name,
|
||||
const char brkt_left,
|
||||
const char brkt_right,
|
||||
char escape)
|
||||
{
|
||||
size_t len = strlen(name);
|
||||
size_t len = name.size();
|
||||
// Shortest bus name is a[0].
|
||||
if (len >= 4
|
||||
// Escaped bus brackets are not buses.
|
||||
&& name[len - 2] != escape
|
||||
&& name[len - 1] == brkt_right) {
|
||||
const char *left = strrchr(name, brkt_left);
|
||||
return left != nullptr;
|
||||
size_t left = name.rfind(brkt_left);
|
||||
return left != std::string_view::npos;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
parseBusName(const char *name,
|
||||
parseBusName(std::string_view name,
|
||||
const char brkt_left,
|
||||
const char brkt_right,
|
||||
const char escape,
|
||||
|
|
@ -61,16 +60,15 @@ parseBusName(const char *name,
|
|||
std::string &bus_name,
|
||||
int &index)
|
||||
{
|
||||
const char brkts_left[2] = {brkt_left, '\0'};
|
||||
const char brkts_right[2] = {brkt_right, '\0'};
|
||||
parseBusName(name, brkts_left, brkts_right, escape,
|
||||
parseBusName(name, std::string_view(&brkt_left, 1),
|
||||
std::string_view(&brkt_right, 1), escape,
|
||||
is_bus, bus_name, index);
|
||||
}
|
||||
|
||||
void
|
||||
parseBusName(const char *name,
|
||||
const char *brkts_left,
|
||||
const char *brkts_right,
|
||||
parseBusName(std::string_view name,
|
||||
std::string_view brkts_left,
|
||||
std::string_view brkts_right,
|
||||
char escape,
|
||||
// Return values.
|
||||
bool &is_bus,
|
||||
|
|
@ -78,30 +76,28 @@ parseBusName(const char *name,
|
|||
int &index)
|
||||
{
|
||||
is_bus = false;
|
||||
size_t len = strlen(name);
|
||||
size_t len = name.size();
|
||||
// Shortest bus name is a[0].
|
||||
if (len >= 4
|
||||
// Escaped bus brackets are not buses.
|
||||
&& name[len - 2] != escape) {
|
||||
char last_ch = name[len - 1];
|
||||
const char *brkt_right_ptr = strchr(brkts_right, last_ch);
|
||||
if (brkt_right_ptr) {
|
||||
size_t brkt_index = brkt_right_ptr - brkts_right;
|
||||
char brkt_left = brkts_left[brkt_index];
|
||||
const char *left = strrchr(name, brkt_left);
|
||||
if (left) {
|
||||
size_t brkt_index = brkts_right.find(last_ch);
|
||||
if (brkt_index != std::string_view::npos) {
|
||||
char brkt_left_ch = brkts_left[brkt_index];
|
||||
size_t left = name.rfind(brkt_left_ch);
|
||||
if (left != std::string_view::npos) {
|
||||
is_bus = true;
|
||||
size_t bus_name_len = left - name;
|
||||
bus_name.append(name, bus_name_len);
|
||||
bus_name.append(name.data(), left);
|
||||
// Simple bus subscript.
|
||||
index = atoi(left + 1);
|
||||
index = std::stoi(std::string(name.substr(left + 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
parseBusName(const char *name,
|
||||
parseBusName(std::string_view name,
|
||||
const char brkt_left,
|
||||
const char brkt_right,
|
||||
char escape,
|
||||
|
|
@ -113,16 +109,15 @@ parseBusName(const char *name,
|
|||
int &to,
|
||||
bool &subscript_wild)
|
||||
{
|
||||
const char brkts_left[2] = {brkt_left, '\0'};
|
||||
const char brkts_right[2] = {brkt_right, '\0'};
|
||||
parseBusName(name, brkts_left, brkts_right, escape,
|
||||
parseBusName(name, std::string_view(&brkt_left, 1),
|
||||
std::string_view(&brkt_right, 1), escape,
|
||||
is_bus, is_range, bus_name, from, to, subscript_wild);
|
||||
}
|
||||
|
||||
void
|
||||
parseBusName(const char *name,
|
||||
const char *brkts_left,
|
||||
const char *brkts_right,
|
||||
parseBusName(std::string_view name,
|
||||
std::string_view brkts_left,
|
||||
std::string_view brkts_right,
|
||||
char escape,
|
||||
// Return values.
|
||||
bool &is_bus,
|
||||
|
|
@ -135,36 +130,31 @@ parseBusName(const char *name,
|
|||
is_bus = false;
|
||||
is_range = false;
|
||||
subscript_wild = false;
|
||||
size_t len = strlen(name);
|
||||
size_t len = name.size();
|
||||
// Shortest bus is a[0].
|
||||
if (len >= 4
|
||||
// Escaped bus brackets are not buses.
|
||||
&& name[len - 2] != escape) {
|
||||
char last_ch = name[len - 1];
|
||||
const char *brkt_right_ptr = strchr(brkts_right, last_ch);
|
||||
if (brkt_right_ptr) {
|
||||
size_t brkt_index = brkt_right_ptr - brkts_right;
|
||||
char brkt_left = brkts_left[brkt_index];
|
||||
const char *left = strrchr(name, brkt_left);
|
||||
if (left) {
|
||||
size_t brkt_index = brkts_right.find(last_ch);
|
||||
if (brkt_index != std::string_view::npos) {
|
||||
char brkt_left_ch = brkts_left[brkt_index];
|
||||
size_t left = name.rfind(brkt_left_ch);
|
||||
if (left != std::string_view::npos) {
|
||||
is_bus = true;
|
||||
bus_name.append(name.data(), left);
|
||||
// Check for bus range.
|
||||
const char range_sep = ':';
|
||||
const char *range = strchr(name, range_sep);
|
||||
if (range) {
|
||||
size_t range = name.find(':', left);
|
||||
if (range != std::string_view::npos) {
|
||||
is_range = true;
|
||||
bus_name.append(name, left - name);
|
||||
// No need to terminate bus subscript because atoi stops
|
||||
// scanning at first non-digit character.
|
||||
from = atoi(left + 1);
|
||||
to = atoi(range + 1);
|
||||
from = std::stoi(std::string(name.substr(left + 1)));
|
||||
to = std::stoi(std::string(name.substr(range + 1)));
|
||||
}
|
||||
else {
|
||||
bus_name.append(name, left - name);
|
||||
if (left[1] == '*')
|
||||
if (left + 1 < len && name[left + 1] == '*')
|
||||
subscript_wild = true;
|
||||
else
|
||||
from = to = atoi(left + 1);
|
||||
from = to = std::stoi(std::string(name.substr(left + 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -172,22 +162,23 @@ parseBusName(const char *name,
|
|||
}
|
||||
|
||||
std::string
|
||||
escapeChars(const char *token,
|
||||
escapeChars(std::string_view token,
|
||||
const char ch1,
|
||||
const char ch2,
|
||||
const char escape)
|
||||
{
|
||||
std::string escaped;
|
||||
for (const char *s = token; *s; s++) {
|
||||
char ch = *s;
|
||||
escaped.reserve(token.size());
|
||||
for (size_t i = 0; i < token.size(); i++) {
|
||||
char ch = token[i];
|
||||
if (ch == escape) {
|
||||
char next_ch = s[1];
|
||||
// Make sure we don't skip the null if escape is the last char.
|
||||
if (next_ch != '\0') {
|
||||
if (i + 1 < token.size()) {
|
||||
escaped += ch;
|
||||
escaped += next_ch;
|
||||
s++;
|
||||
escaped += token[i + 1];
|
||||
i++;
|
||||
}
|
||||
else
|
||||
escaped += ch;
|
||||
}
|
||||
else if (ch == ch1 || ch == ch2) {
|
||||
escaped += escape;
|
||||
|
|
|
|||
|
|
@ -640,28 +640,27 @@ SdcNetwork::SdcNetwork(Network *network) :
|
|||
// Translate sta namespace to sdc namespace.
|
||||
// Remove all escapes.
|
||||
const char *
|
||||
SdcNetwork::staToSdc(const char *sta_name) const
|
||||
SdcNetwork::staToSdc(std::string_view sta_name) const
|
||||
{
|
||||
char escape = pathEscape();
|
||||
char *sdc_name = makeTmpString(strlen(sta_name) + 1);
|
||||
char *d = sdc_name;
|
||||
for (const char *s = sta_name; *s; s++) {
|
||||
char ch = s[0];
|
||||
size_t sta_length = sta_name.length();
|
||||
std::string sdc_name;
|
||||
for (size_t i = 0; i < sta_length; i++) {
|
||||
char ch = sta_name[i];
|
||||
if (ch == escape) {
|
||||
char next_ch = s[1];
|
||||
char next_ch = sta_name[i + 1];
|
||||
// Escaped escape.
|
||||
if (next_ch == escape) {
|
||||
*d++ = ch;
|
||||
*d++ = next_ch;
|
||||
s++;
|
||||
sdc_name += ch;
|
||||
sdc_name += next_ch;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
// Non escape.
|
||||
*d++ = ch;
|
||||
sdc_name += ch;
|
||||
}
|
||||
*d++ = '\0';
|
||||
return sdc_name;
|
||||
return makeTmpString(sdc_name);
|
||||
}
|
||||
|
||||
Port *
|
||||
|
|
@ -680,11 +679,11 @@ SdcNetwork::findPort(const Cell *cell,
|
|||
port = network_->findPort(cell, escaped1.c_str());
|
||||
if (port == nullptr) {
|
||||
// Try escaping base foo\[0\][1]
|
||||
std::string escaped2;
|
||||
std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this);
|
||||
stringPrint(escaped2, "%s[%d]",
|
||||
escaped_bus_name.c_str(),
|
||||
index);
|
||||
std::string escaped2 = escaped_bus_name
|
||||
+ '['
|
||||
+ std::to_string(index)
|
||||
+ ']';
|
||||
port = network_->findPort(cell, escaped2.c_str());
|
||||
}
|
||||
}
|
||||
|
|
@ -966,8 +965,10 @@ SdcNetwork::findPin(const Instance *instance,
|
|||
if (pin == nullptr) {
|
||||
// Try escaping base foo\[0\][1]
|
||||
std::string escaped_bus_name = escapeBrackets(bus_name.c_str(), this);
|
||||
std::string escaped2;
|
||||
stringPrint(escaped2, "%s[%d]", escaped_bus_name.c_str(), index);
|
||||
std::string escaped2 = escaped_bus_name
|
||||
+ '['
|
||||
+ std::to_string(index)
|
||||
+ ']';
|
||||
pin = network_->findPin(instance, escaped2.c_str());
|
||||
}
|
||||
}
|
||||
|
|
@ -1149,46 +1150,39 @@ SdcNetwork::parsePath(const char *path,
|
|||
const char *&path_tail) const
|
||||
{
|
||||
Instance *parent = topInstance();
|
||||
std::string inst_path;
|
||||
// Leave room to escape all the dividers and '\0'.
|
||||
int inst_path_length = path_length + divider_count + 1;
|
||||
char *inst_path = new char[inst_path_length];
|
||||
inst_path.reserve(path_length + divider_count + 1);
|
||||
inst = nullptr;
|
||||
path_tail = path;
|
||||
char *p = inst_path;
|
||||
for (const char *s = path; *s; s++) {
|
||||
char ch = *s;
|
||||
if (ch == escape_) {
|
||||
// Make sure we don't skip the null if escape is the last char.
|
||||
if (s[1] != '\0') {
|
||||
*p++ = ch;
|
||||
*p++ = s[1];
|
||||
inst_path += ch;
|
||||
inst_path += s[1];
|
||||
s++;
|
||||
}
|
||||
}
|
||||
else if (ch == divider_) {
|
||||
// Terminate the sub-path up to this divider.
|
||||
*p = '\0';
|
||||
Instance *child = findChild(parent, inst_path);
|
||||
Instance *child = findChild(parent, inst_path.c_str());
|
||||
if (child) {
|
||||
// Found an instance for the sub-path up to this divider.
|
||||
parent = inst = child;
|
||||
// Reset the instance path.
|
||||
p = inst_path;
|
||||
inst_path.clear();
|
||||
path_tail = s + 1;
|
||||
}
|
||||
else {
|
||||
// No match for sub-path. Escape the divider and keep looking.
|
||||
*p++ = escape_;
|
||||
*p++ = divider_;
|
||||
inst_path += escape_;
|
||||
inst_path += divider_;
|
||||
}
|
||||
}
|
||||
else
|
||||
*p++ = ch;
|
||||
if (p - inst_path + 1 > inst_path_length)
|
||||
report_->critical(1500, "inst path std::string lenth estimate busted");
|
||||
inst_path += ch;
|
||||
}
|
||||
*p = '\0';
|
||||
stringDelete(inst_path);
|
||||
}
|
||||
|
||||
// Helper to visit instance path matches.
|
||||
|
|
@ -1207,11 +1201,9 @@ SdcNetwork::visitMatches(const Instance *parent,
|
|||
{
|
||||
int divider_count, path_length;
|
||||
scanPath(pattern->pattern(), divider_count, path_length);
|
||||
|
||||
std::string inst_path;
|
||||
// Leave room to escape all the dividers and '\0'.
|
||||
int inst_path_length = path_length + divider_count + 1;
|
||||
char *inst_path = new char[inst_path_length];
|
||||
char *p = inst_path;
|
||||
inst_path.reserve(path_length + divider_count + 1);
|
||||
bool has_brkts = false;
|
||||
bool found_match = false;
|
||||
for (const char *s = pattern->pattern(); *s; s++) {
|
||||
|
|
@ -1219,20 +1211,18 @@ SdcNetwork::visitMatches(const Instance *parent,
|
|||
if (ch == escape_) {
|
||||
// Make sure we don't skip the null if escape is the last char.
|
||||
if (s[1] != '\0') {
|
||||
*p++ = ch;
|
||||
*p++ = s[1];
|
||||
inst_path += ch;
|
||||
inst_path += s[1];
|
||||
s++;
|
||||
}
|
||||
}
|
||||
else if (ch == divider_) {
|
||||
// Terminate the sub-path up to this divider.
|
||||
*p = '\0';
|
||||
PatternMatch matcher(inst_path, pattern);
|
||||
PatternMatch matcher(inst_path.c_str(), pattern);
|
||||
InstanceSeq matches;
|
||||
network_->findChildrenMatching(parent, &matcher, matches);
|
||||
if (has_brkts && matches.empty()) {
|
||||
// Look for matches after escaping brackets.
|
||||
std::string escaped_brkts = escapeBrackets(inst_path, this);
|
||||
std::string escaped_brkts = escapeBrackets(inst_path.c_str(), this);
|
||||
const PatternMatch escaped_pattern(escaped_brkts, pattern);
|
||||
network_->findChildrenMatching(parent, &escaped_pattern, matches);
|
||||
}
|
||||
|
|
@ -1245,29 +1235,25 @@ SdcNetwork::visitMatches(const Instance *parent,
|
|||
found_match |= visitMatches(match, &tail_pattern, visit_tail);
|
||||
}
|
||||
// Escape the divider and keep looking.
|
||||
*p++ = escape_;
|
||||
*p++ = divider_;
|
||||
inst_path += escape_;
|
||||
inst_path += divider_;
|
||||
}
|
||||
else {
|
||||
if (ch == '[' || ch == ']')
|
||||
has_brkts = true;
|
||||
*p++ = ch;
|
||||
inst_path += ch;
|
||||
}
|
||||
if (p - inst_path + 1 > inst_path_length)
|
||||
report_->critical(1501, "inst path std::string lenth estimate exceeded");
|
||||
}
|
||||
*p = '\0';
|
||||
if (!found_match) {
|
||||
PatternMatch tail_pattern(inst_path, pattern);
|
||||
PatternMatch tail_pattern(inst_path.c_str(), pattern);
|
||||
found_match |= visit_tail(parent, &tail_pattern);
|
||||
if (!found_match && has_brkts) {
|
||||
// Look for matches after escaping brackets.
|
||||
std::string escaped_path = escapeBrackets(inst_path, this);
|
||||
std::string escaped_path = escapeBrackets(inst_path.c_str(), this);
|
||||
const PatternMatch escaped_tail(escaped_path, pattern);
|
||||
found_match |= visit_tail(parent, &escaped_tail);
|
||||
}
|
||||
}
|
||||
stringDelete(inst_path);
|
||||
return found_match;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,35 +34,34 @@ namespace sta {
|
|||
constexpr char verilog_escape = '\\';
|
||||
|
||||
static std::string
|
||||
staToVerilog(const char *sta_name);
|
||||
staToVerilog(std::string sta_name);
|
||||
static std::string
|
||||
staToVerilog2(const char *sta_name);
|
||||
staToVerilog2(std::string sta_name);
|
||||
static std::string
|
||||
verilogToSta(const std::string *verilog_name);
|
||||
verilogToSta(const std::string verilog_name);
|
||||
|
||||
std::string
|
||||
cellVerilogName(const char *sta_name)
|
||||
cellVerilogName(std::string sta_name)
|
||||
{
|
||||
return staToVerilog(sta_name);
|
||||
}
|
||||
|
||||
std::string
|
||||
instanceVerilogName(const char *sta_name)
|
||||
instanceVerilogName(std::string sta_name)
|
||||
{
|
||||
return staToVerilog(sta_name);
|
||||
}
|
||||
|
||||
std::string
|
||||
netVerilogName(const char *sta_name)
|
||||
netVerilogName(std::string sta_name)
|
||||
{
|
||||
bool is_bus;
|
||||
std::string bus_name;
|
||||
int index;
|
||||
parseBusName(sta_name, '[', ']', verilog_escape, is_bus, bus_name, index);
|
||||
parseBusName(sta_name.c_str(), '[', ']', verilog_escape, is_bus, bus_name, index);
|
||||
if (is_bus) {
|
||||
std::string bus_vname = staToVerilog(bus_name.c_str());
|
||||
std::string vname;
|
||||
stringPrint(vname, "%s[%d]", bus_vname.c_str(), index);
|
||||
std::string vname = bus_vname + '[' + std::to_string(index) + ']';
|
||||
return vname;
|
||||
}
|
||||
else
|
||||
|
|
@ -70,27 +69,28 @@ netVerilogName(const char *sta_name)
|
|||
}
|
||||
|
||||
std::string
|
||||
portVerilogName(const char *sta_name)
|
||||
portVerilogName(std::string sta_name)
|
||||
{
|
||||
return staToVerilog2(sta_name);
|
||||
}
|
||||
|
||||
static std::string
|
||||
staToVerilog(const char *sta_name)
|
||||
staToVerilog(std::string sta_name)
|
||||
{
|
||||
// Leave room for leading escape and trailing space if the name
|
||||
// needs to be escaped.
|
||||
// Assume the name has to be escaped and start copying while scanning.
|
||||
std::string escaped_name = "\\";
|
||||
bool escaped = false;
|
||||
for (const char *s = sta_name; *s ; s++) {
|
||||
char ch = s[0];
|
||||
size_t sta_length = sta_name.size();
|
||||
for (size_t i = 0; i < sta_length; i++) {
|
||||
char ch = sta_name[i];
|
||||
if (ch == verilog_escape) {
|
||||
escaped = true;
|
||||
char next_ch = s[1];
|
||||
char next_ch = sta_name[i + 1];
|
||||
if (next_ch == verilog_escape) {
|
||||
escaped_name += next_ch;
|
||||
s++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -105,11 +105,11 @@ staToVerilog(const char *sta_name)
|
|||
return escaped_name;
|
||||
}
|
||||
else
|
||||
return std::string(sta_name);
|
||||
return sta_name;
|
||||
}
|
||||
|
||||
static std::string
|
||||
staToVerilog2(const char *sta_name)
|
||||
staToVerilog2(std::string sta_name)
|
||||
{
|
||||
constexpr char bus_brkt_left = '[';
|
||||
constexpr char bus_brkt_right = ']';
|
||||
|
|
@ -118,14 +118,15 @@ staToVerilog2(const char *sta_name)
|
|||
std::string escaped_name = "\\";
|
||||
// Assume the name has to be escaped and start copying while scanning.
|
||||
bool escaped = false;
|
||||
for (const char *s = sta_name; *s ; s++) {
|
||||
char ch = s[0];
|
||||
size_t sta_length = sta_name.size();
|
||||
for (size_t i = 0; i < sta_length; i++) {
|
||||
char ch = sta_name[i];
|
||||
if (ch == verilog_escape) {
|
||||
escaped = true;
|
||||
char next_ch = s[1];
|
||||
char next_ch = sta_name[i + 1];
|
||||
if (next_ch == verilog_escape) {
|
||||
escaped_name += next_ch;
|
||||
s++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -142,50 +143,50 @@ staToVerilog2(const char *sta_name)
|
|||
return escaped_name;
|
||||
}
|
||||
else
|
||||
return std::string(sta_name);
|
||||
return sta_name;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string
|
||||
moduleVerilogToSta(const std::string *module_name)
|
||||
moduleVerilogToSta(std::string module_name)
|
||||
{
|
||||
return verilogToSta(module_name);
|
||||
}
|
||||
|
||||
std::string
|
||||
instanceVerilogToSta(const std::string *inst_name)
|
||||
instanceVerilogToSta(std::string inst_name)
|
||||
{
|
||||
return verilogToSta(inst_name);
|
||||
}
|
||||
|
||||
std::string
|
||||
netVerilogToSta(const std::string *net_name)
|
||||
netVerilogToSta(std::string net_name)
|
||||
{
|
||||
return verilogToSta(net_name);
|
||||
}
|
||||
|
||||
std::string
|
||||
portVerilogToSta(const std::string *port_name)
|
||||
portVerilogToSta(std::string port_name)
|
||||
{
|
||||
return verilogToSta(port_name);
|
||||
}
|
||||
|
||||
static std::string
|
||||
verilogToSta(const std::string *verilog_name)
|
||||
verilogToSta(std::string verilog_name)
|
||||
{
|
||||
if (verilog_name->front() == '\\') {
|
||||
if (verilog_name.front() == '\\') {
|
||||
constexpr char divider = '/';
|
||||
constexpr char bus_brkt_left = '[';
|
||||
constexpr char bus_brkt_right = ']';
|
||||
|
||||
size_t verilog_name_length = verilog_name->size();
|
||||
if (isspace(verilog_name->back()))
|
||||
size_t verilog_name_length = verilog_name.size();
|
||||
if (isspace(verilog_name.back()))
|
||||
verilog_name_length--;
|
||||
std::string sta_name;
|
||||
// Ignore leading '\'.
|
||||
for (size_t i = 1; i < verilog_name_length; i++) {
|
||||
char ch = verilog_name->at(i);
|
||||
char ch = verilog_name[i];
|
||||
if (ch == bus_brkt_left
|
||||
|| ch == bus_brkt_right
|
||||
|| ch == divider
|
||||
|
|
@ -197,7 +198,7 @@ verilogToSta(const std::string *verilog_name)
|
|||
return sta_name;
|
||||
}
|
||||
else
|
||||
return std::string(*verilog_name);
|
||||
return verilog_name;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
|||
|
|
@ -410,8 +410,10 @@ const char *
|
|||
ConcreteParasiticNode::name(const Network *network) const
|
||||
{
|
||||
if (is_net_) {
|
||||
const char *net_name = network->pathName(net_pin_.net_);
|
||||
return stringPrintTmp("%s:%d", net_name, id_);
|
||||
std::string name = std::string(network->pathName(net_pin_.net_))
|
||||
+ ':'
|
||||
+ std::to_string(id_);
|
||||
return makeTmpString(name);
|
||||
}
|
||||
else
|
||||
return network->pathName(net_pin_.pin_);
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "Parasitics.hh"
|
||||
|
|
@ -49,38 +49,29 @@ Parasitics::report(const Parasitic *parasitic) const
|
|||
{
|
||||
if (isParasiticNetwork(parasitic)) {
|
||||
const Unit *cap_unit = units_->capacitanceUnit();
|
||||
report_->reportLine("Net %s %s",
|
||||
network_->pathName(net(parasitic)),
|
||||
cap_unit->asString(capacitance(parasitic)));
|
||||
report_->reportLine("Nodes:");
|
||||
report_->report("Net {} {}", network_->pathName(net(parasitic)),
|
||||
cap_unit->asString(capacitance(parasitic)));
|
||||
report_->report("Nodes:");
|
||||
for (ParasiticNode *node : nodes(parasitic))
|
||||
report_->reportLine("%s%s %s",
|
||||
name(node),
|
||||
isExternal(node) ? " (ext)" : "",
|
||||
cap_unit->asString(nodeGndCap(node)));
|
||||
report_->reportLine("Resistors:");
|
||||
report_->report("{}{} {}", name(node), isExternal(node) ? " (ext)" : "",
|
||||
cap_unit->asString(nodeGndCap(node)));
|
||||
report_->report("Resistors:");
|
||||
for (ParasiticResistor *res : resistors(parasitic)) {
|
||||
ParasiticNode *node1 = this->node1(res);
|
||||
ParasiticNode *node2 = this->node2(res);
|
||||
report_->reportLine("%zu %s%s %s%s %s",
|
||||
id(res),
|
||||
name(node1),
|
||||
isExternal(node1) ? " (ext)" : "",
|
||||
name(node2),
|
||||
isExternal(node2) ? " (ext)" : "",
|
||||
units_->resistanceUnit()->asString(value(res)));
|
||||
report_->report("{} {}{} {}{} {}", id(res), name(node1),
|
||||
isExternal(node1) ? " (ext)" : "", name(node2),
|
||||
isExternal(node2) ? " (ext)" : "",
|
||||
units_->resistanceUnit()->asString(value(res)));
|
||||
}
|
||||
report_->reportLine("Coupling Capacitors:");
|
||||
report_->report("Coupling Capacitors:");
|
||||
for (ParasiticCapacitor *cap : capacitors(parasitic)) {
|
||||
ParasiticNode *node1 = this->node1(cap);
|
||||
ParasiticNode *node2 = this->node2(cap);
|
||||
report_->reportLine("%zu %s%s %s%s %s",
|
||||
id(cap),
|
||||
name(node1),
|
||||
isExternal(node1) ? " (ext)" : "",
|
||||
name(node2),
|
||||
isExternal(node2) ? " (ext)" : "",
|
||||
cap_unit->asString(value(cap)));
|
||||
report_->report("{} {}{} {}{} {}", id(cap), name(node1),
|
||||
isExternal(node1) ? " (ext)" : "", name(node2),
|
||||
isExternal(node2) ? " (ext)" : "",
|
||||
cap_unit->asString(value(cap)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -138,10 +129,10 @@ Parasitics::parasiticNodeResistorMap(const Parasitic *parasitic) const
|
|||
return resistor_map;
|
||||
}
|
||||
|
||||
ParasiticNodeCapacitorMap
|
||||
ParasiticNodeCapacitorMap
|
||||
Parasitics::parasiticNodeCapacitorMap(const Parasitic *parasitic) const
|
||||
{
|
||||
ParasiticNodeCapacitorMap capacitor_map;
|
||||
ParasiticNodeCapacitorMap capacitor_map;
|
||||
for (ParasiticCapacitor *capacitor : capacitors(parasitic)) {
|
||||
ParasiticNode *n1 = node1(capacitor);
|
||||
ParasiticNode *n2 = node2(capacitor);
|
||||
|
|
@ -186,9 +177,8 @@ Parasitics::reduceToPiElmore(const Parasitic *parasitic,
|
|||
const Scene *scene,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
return sta::reduceToPiElmore(parasitic, drvr_pin, rf,
|
||||
coupling_cap_factor_,
|
||||
scene, min_max, this);
|
||||
return sta::reduceToPiElmore(parasitic, drvr_pin, rf, coupling_cap_factor_, scene,
|
||||
min_max, this);
|
||||
}
|
||||
|
||||
Parasitic *
|
||||
|
|
@ -198,8 +188,7 @@ Parasitics::reduceToPiPoleResidue2(const Parasitic *parasitic,
|
|||
const Scene *scene,
|
||||
const MinMax *min_max)
|
||||
{
|
||||
return sta::reduceToPiPoleResidue2(parasitic, drvr_pin, rf,
|
||||
coupling_cap_factor_,
|
||||
return sta::reduceToPiPoleResidue2(parasitic, drvr_pin, rf, coupling_cap_factor_,
|
||||
scene, min_max, this);
|
||||
}
|
||||
|
||||
|
|
@ -217,10 +206,9 @@ Parasitics::estimatePiElmore(const Pin *drvr_pin,
|
|||
EstimateParasitics estimate(this);
|
||||
float c2, rpi, c1, elmore_res, elmore_cap;
|
||||
bool elmore_use_load_cap;
|
||||
estimate.estimatePiElmore(drvr_pin, rf, wireload, fanout, net_pin_cap,
|
||||
scene, min_max,
|
||||
c2, rpi, c1,
|
||||
elmore_res, elmore_cap, elmore_use_load_cap);
|
||||
estimate.estimatePiElmore(drvr_pin, rf, wireload, fanout, net_pin_cap, scene,
|
||||
min_max, c2, rpi, c1, elmore_res, elmore_cap,
|
||||
elmore_use_load_cap);
|
||||
|
||||
if (c1 > 0.0 || c2 > 0.0) {
|
||||
Parasitic *parasitic = makePiElmore(drvr_pin, rf, min_max, c2, rpi, c1);
|
||||
|
|
@ -265,19 +253,19 @@ Parasitics::makeWireloadNetwork(const Pin *drvr_pin,
|
|||
if (op_cond)
|
||||
tree = op_cond->wireloadTree();
|
||||
switch (tree) {
|
||||
case WireloadTree::worst_case:
|
||||
makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
case WireloadTree::balanced:
|
||||
makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
case WireloadTree::best_case:
|
||||
case WireloadTree::unknown:
|
||||
makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
case WireloadTree::worst_case:
|
||||
makeWireloadNetworkWorst(parasitic, drvr_pin, net, wireload_cap,
|
||||
wireload_res, fanout);
|
||||
break;
|
||||
case WireloadTree::balanced:
|
||||
makeWireloadNetworkBalanced(parasitic, drvr_pin, wireload_cap, wireload_res,
|
||||
fanout);
|
||||
break;
|
||||
case WireloadTree::best_case:
|
||||
case WireloadTree::unknown:
|
||||
makeWireloadNetworkBest(parasitic, drvr_pin, wireload_cap, wireload_res,
|
||||
fanout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parasitic;
|
||||
|
|
@ -298,12 +286,10 @@ Parasitics::makeWireloadNetworkWorst(Parasitic *parasitic,
|
|||
ParasiticNode *load_node = ensureParasiticNode(parasitic, net, 0, network_);
|
||||
makeResistor(parasitic, resistor_index++, wireload_res, drvr_node, load_node);
|
||||
incrCap(load_node, wireload_cap);
|
||||
PinConnectedPinIterator *load_iter =
|
||||
network_->connectedPinIterator(drvr_pin);
|
||||
PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin);
|
||||
while (load_iter->hasNext()) {
|
||||
const Pin *load_pin = load_iter->next();
|
||||
if (load_pin != drvr_pin
|
||||
&& network_->isLoad(load_pin)) {
|
||||
if (load_pin != drvr_pin && network_->isLoad(load_pin)) {
|
||||
ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_);
|
||||
makeResistor(parasitic, resistor_index++, 0.0, load_node, load_node1);
|
||||
}
|
||||
|
|
@ -320,13 +306,11 @@ Parasitics::makeWireloadNetworkBest(Parasitic *parasitic,
|
|||
{
|
||||
ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_);
|
||||
incrCap(drvr_node, wireload_cap);
|
||||
PinConnectedPinIterator *load_iter =
|
||||
network_->connectedPinIterator(drvr_pin);
|
||||
PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin);
|
||||
size_t resistor_index = 1;
|
||||
while (load_iter->hasNext()) {
|
||||
const Pin *load_pin = load_iter->next();
|
||||
if (load_pin != drvr_pin
|
||||
&& network_->isLoad(load_pin)) {
|
||||
if (load_pin != drvr_pin && network_->isLoad(load_pin)) {
|
||||
ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_);
|
||||
makeResistor(parasitic, resistor_index++, 0.0, drvr_node, load_node1);
|
||||
}
|
||||
|
|
@ -345,15 +329,13 @@ Parasitics::makeWireloadNetworkBalanced(Parasitic *parasitic,
|
|||
float fanout_cap = wireload_cap / fanout;
|
||||
float fanout_res = wireload_res / fanout;
|
||||
ParasiticNode *drvr_node = ensureParasiticNode(parasitic, drvr_pin, network_);
|
||||
PinConnectedPinIterator *load_iter =
|
||||
network_->connectedPinIterator(drvr_pin);
|
||||
PinConnectedPinIterator *load_iter = network_->connectedPinIterator(drvr_pin);
|
||||
size_t resistor_index = 1;
|
||||
while (load_iter->hasNext()) {
|
||||
const Pin *load_pin = load_iter->next();
|
||||
if (load_pin != drvr_pin
|
||||
&& network_->isLoad(load_pin)) {
|
||||
if (load_pin != drvr_pin && network_->isLoad(load_pin)) {
|
||||
ParasiticNode *load_node1 = ensureParasiticNode(parasitic, load_pin, network_);
|
||||
makeResistor(parasitic, resistor_index++, fanout_res, drvr_node, load_node1);
|
||||
makeResistor(parasitic, resistor_index++, fanout_res, drvr_node, load_node1);
|
||||
incrCap(load_node1, fanout_cap);
|
||||
}
|
||||
}
|
||||
|
|
@ -391,12 +373,10 @@ ParasiticNodeLess::operator()(const ParasiticNode *node1,
|
|||
unsigned id1 = parasitics_->netId(node1);
|
||||
unsigned id2 = parasitics_->netId(node2);
|
||||
return (pin1 == nullptr && pin2)
|
||||
|| (pin1 && pin2
|
||||
&& network_->id(pin1) < network_->id(pin2))
|
||||
|| (pin1 == nullptr && pin2 == nullptr
|
||||
&& (network_->id(net1) < network_->id(net2)
|
||||
|| (net1 == net2
|
||||
&& id1 < id2)));
|
||||
|| (pin1 && pin2 && network_->id(pin1) < network_->id(pin2))
|
||||
|| (pin1 == nullptr && pin2 == nullptr
|
||||
&& (network_->id(net1) < network_->id(net2)
|
||||
|| (net1 == net2 && id1 < id2)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ ReduceToPi::reduceToPi(const Parasitic *parasitic_network,
|
|||
rpi = -y3 * y3 / (y2 * y2 * y2);
|
||||
}
|
||||
debugPrint(debug_, "parasitic_reduce", 2,
|
||||
" Pi model c2=%.3g rpi=%.3g c1=%.3g max_r=%.3g",
|
||||
" Pi model c2={:.3g} rpi={:.3g} c1={:.3g} max_r={:.3g}",
|
||||
c2, rpi, c1, max_resistance);
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin,
|
|||
&& resistor != from_res) {
|
||||
if (isVisited(onode)) {
|
||||
// Resistor loop.
|
||||
debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor %zu",
|
||||
debugPrint(debug_, "parasitic_reduce", 2, " loop detected thru resistor {}",
|
||||
parasitics_->id(resistor));
|
||||
markLoopResistor(resistor);
|
||||
}
|
||||
|
|
@ -208,7 +208,7 @@ ReduceToPi::reducePiDfs(const Pin *drvr_pin,
|
|||
setDownstreamCap(node, dwn_cap);
|
||||
leave(node);
|
||||
debugPrint(debug_, "parasitic_reduce", 3,
|
||||
" node %s y1=%.3g y2=%.3g y3=%.3g cap=%.3g",
|
||||
" node {} y1={:.3g} y2={:.3g} y3={:.3g} cap={:.3g}",
|
||||
parasitics_->name(node), y1, y2, y3, dwn_cap);
|
||||
}
|
||||
|
||||
|
|
@ -309,10 +309,10 @@ reduceToPiElmore(const Parasitic *parasitic_network,
|
|||
ParasiticNode *drvr_node =
|
||||
parasitics->findParasiticNode(parasitic_network, drvr_pin);
|
||||
if (drvr_node) {
|
||||
debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s %s %s",
|
||||
debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver {} {} {}",
|
||||
sta->network()->pathName(drvr_pin),
|
||||
rf->shortName(),
|
||||
min_max->to_string().c_str());
|
||||
min_max->to_string());
|
||||
ReduceToPiElmore reducer(sta);
|
||||
return reducer.makePiElmore(parasitic_network, drvr_pin, drvr_node,
|
||||
coupling_cap_factor, rf, scene, min_max);
|
||||
|
|
@ -356,7 +356,7 @@ ReduceToPiElmore::reduceElmoreDfs(const Pin *drvr_pin,
|
|||
const Pin *pin = parasitics_->pin(node);
|
||||
if (from_res && pin) {
|
||||
if (network_->isLoad(pin)) {
|
||||
debugPrint(debug_, "parasitic_reduce", 2, " Load %s elmore=%.3g",
|
||||
debugPrint(debug_, "parasitic_reduce", 2, " Load {} elmore={:.3g}",
|
||||
network_->pathName(pin),
|
||||
elmore);
|
||||
parasitics_->setElmore(pi_elmore, pin, elmore);
|
||||
|
|
@ -456,7 +456,7 @@ reduceToPiPoleResidue2(const Parasitic *parasitic_network,
|
|||
ParasiticNode *drvr_node =
|
||||
parasitics->findParasiticNode(parasitic_network, drvr_pin);
|
||||
if (drvr_node) {
|
||||
debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver %s",
|
||||
debugPrint(sta->debug(), "parasitic_reduce", 1, "Reduce driver {}",
|
||||
sta->network()->pathName(drvr_pin));
|
||||
ReduceToPiPoleResidue2 reducer(sta);
|
||||
return reducer.makePiPoleResidue2(parasitic_network, drvr_pin, drvr_node,
|
||||
|
|
@ -564,7 +564,7 @@ ReduceToPiPoleResidue2::findBranchCurrents(const Pin *drvr_pin,
|
|||
leave(node);
|
||||
if (from_res) {
|
||||
setCurrent(from_res, branch_i);
|
||||
debugPrint(debug_, "parasitic_reduce", 3, " res i=%.3g", branch_i);
|
||||
debugPrint(debug_, "parasitic_reduce", 3, " res i={:.3g}", branch_i);
|
||||
}
|
||||
return branch_i;
|
||||
}
|
||||
|
|
@ -589,7 +589,7 @@ ReduceToPiPoleResidue2::findMoments(const Pin *drvr_pin,
|
|||
double r_volt = r * current(resistor);
|
||||
double onode_volt = from_volt - r_volt;
|
||||
setMoment(onode, onode_volt, moment_index);
|
||||
debugPrint(debug_, "parasitic_reduce", 3, " moment %s %d %.3g",
|
||||
debugPrint(debug_, "parasitic_reduce", 3, " moment {} {} {:.3g}",
|
||||
parasitics_->name(onode),
|
||||
moment_index,
|
||||
onode_volt);
|
||||
|
|
@ -655,7 +655,7 @@ ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue,
|
|||
|| m1 / m2 == m2 / m3) {
|
||||
double p1 = -1.0 / m1;
|
||||
double k1 = 1.0;
|
||||
debugPrint(debug_, "parasitic_reduce", 3, " load %s p1=%.3g k1=%.3g",
|
||||
debugPrint(debug_, "parasitic_reduce", 3, " load {} p1={:.3g} k1={:.3g}",
|
||||
network_->pathName(load_pin), p1, k1);
|
||||
ComplexFloatSeq *poles = new ComplexFloatSeq(1);
|
||||
ComplexFloatSeq *residues = new ComplexFloatSeq(1);
|
||||
|
|
@ -675,7 +675,7 @@ ReduceToPiPoleResidue2::findPolesResidues(Parasitic *pi_pole_residue,
|
|||
k1 = k;
|
||||
}
|
||||
debugPrint(debug_, "parasitic_reduce", 3,
|
||||
" load %s p1=%.3g p2=%.3g k1=%.3g k2=%.3g",
|
||||
" load {} p1={:.3g} p2={:.3g} k1={:.3g} k2={:.3g}",
|
||||
network_->pathName(load_pin), p1, p2, k1, k2);
|
||||
|
||||
ComplexFloatSeq *poles = new ComplexFloatSeq(2);
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "ReportParasiticAnnotation.hh"
|
||||
|
|
@ -65,8 +65,8 @@ reportParasiticAnnotation(Parasitics *parasitics,
|
|||
const Scene *scene,
|
||||
StaState *sta)
|
||||
{
|
||||
ReportParasiticAnnotation report_annotation(parasitics, report_unannotated,
|
||||
scene, sta);
|
||||
ReportParasiticAnnotation report_annotation(parasitics, report_unannotated, scene,
|
||||
sta);
|
||||
report_annotation.report();
|
||||
}
|
||||
|
||||
|
|
@ -92,25 +92,26 @@ ReportParasiticAnnotation::report()
|
|||
void
|
||||
ReportParasiticAnnotation::reportAnnotationCounts()
|
||||
{
|
||||
report_->reportLine("Found %zu unannotated drivers.", unannotated_.size());
|
||||
report_->report("Found {} unannotated drivers.", unannotated_.size());
|
||||
if (report_unannotated_) {
|
||||
sort(unannotated_, PinPathNameLess(network_));
|
||||
for (const Pin *drvr_pin : unannotated_)
|
||||
report_->reportLine(" %s", network_->pathName(drvr_pin));
|
||||
report_->report(" {}", network_->pathName(drvr_pin));
|
||||
}
|
||||
|
||||
report_->reportLine("Found %zu partially unannotated drivers.",
|
||||
partially_annotated_.size());
|
||||
report_->report("Found {} partially unannotated drivers.",
|
||||
partially_annotated_.size());
|
||||
if (report_unannotated_) {
|
||||
sort(partially_annotated_, PinPathNameLess(network_));
|
||||
for (const Pin *drvr_pin : partially_annotated_) {
|
||||
report_->reportLine(" %s", network_->pathName(drvr_pin));
|
||||
report_->report(" {}", network_->pathName(drvr_pin));
|
||||
|
||||
Parasitic *parasitic = parasitics_->findParasiticNetwork(drvr_pin);
|
||||
if (parasitic) {
|
||||
PinSet unannotated_loads = parasitics_->unannotatedLoads(parasitic, drvr_pin);
|
||||
PinSet unannotated_loads =
|
||||
parasitics_->unannotatedLoads(parasitic, drvr_pin);
|
||||
for (const Pin *load_pin : unannotated_loads)
|
||||
report_->reportLine(" %s", network_->pathName(load_pin));
|
||||
report_->report(" {}", network_->pathName(load_pin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -124,21 +125,20 @@ ReportParasiticAnnotation::findCounts()
|
|||
Vertex *vertex = vertex_iter.next();
|
||||
Pin *pin = vertex->pin();
|
||||
PortDirection *dir = network_->direction(pin);
|
||||
if (vertex->isDriver(network_)
|
||||
&& !dir->isInternal()) {
|
||||
if (vertex->isDriver(network_) && !dir->isInternal()) {
|
||||
Parasitic *parasitic = parasitics_->findParasiticNetwork(pin);
|
||||
if (parasitic == nullptr)
|
||||
parasitic = arc_delay_calc_->findParasitic(pin, RiseFall::rise(),
|
||||
scene_, min_max_);
|
||||
parasitic =
|
||||
arc_delay_calc_->findParasitic(pin, RiseFall::rise(), scene_, min_max_);
|
||||
if (parasitic) {
|
||||
PinSet unannotated_loads = parasitics_->unannotatedLoads(parasitic, pin);
|
||||
if (unannotated_loads.size() > 0)
|
||||
partially_annotated_.push_back(pin);
|
||||
}
|
||||
else
|
||||
else
|
||||
unannotated_.push_back(pin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ void
|
|||
sta::SpefParse::error(const location_type &loc,
|
||||
const std::string &msg)
|
||||
{
|
||||
reader->report()->fileError(164,reader->filename().c_str(),
|
||||
loc.begin.line,"%s",msg.c_str());
|
||||
reader->report()->fileError(1670,reader->filename(), loc.begin. line, "{}", msg);
|
||||
}
|
||||
%}
|
||||
|
||||
|
|
@ -831,7 +830,7 @@ pos_integer:
|
|||
INTEGER
|
||||
{ int value = $1;
|
||||
if (value < 0)
|
||||
reader->warn(1525, "%d is not positive.", value);
|
||||
reader->warn(1525, "{} is not positive.", value);
|
||||
$$ = value;
|
||||
}
|
||||
;
|
||||
|
|
@ -840,13 +839,13 @@ pos_number:
|
|||
INTEGER
|
||||
{ float value = static_cast<float>($1);
|
||||
if (value < 0)
|
||||
reader->warn(1526, "%.4f is not positive.", value);
|
||||
reader->warn(1526, "{:.4f} is not positive.", value);
|
||||
$$ = value;
|
||||
}
|
||||
| FLOAT
|
||||
{ float value = static_cast<float>($1);
|
||||
if (value < 0)
|
||||
reader->warn(1527, "%.4f is not positive.", value);
|
||||
reader->warn(1527, "{:.4f} is not positive.", value);
|
||||
$$ = value;
|
||||
}
|
||||
;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#include "SpefReader.hh"
|
||||
|
|
@ -55,9 +55,8 @@ readSpefFile(const std::string &filename,
|
|||
Parasitics *parasitics,
|
||||
StaState *sta)
|
||||
{
|
||||
SpefReader reader(filename, instance, pin_cap_included,
|
||||
keep_coupling_caps, coupling_cap_factor,
|
||||
reduce, scene, min_max, parasitics, sta);
|
||||
SpefReader reader(filename, instance, pin_cap_included, keep_coupling_caps,
|
||||
coupling_cap_factor, reduce, scene, min_max, parasitics, sta);
|
||||
bool success = reader.read();
|
||||
return success;
|
||||
}
|
||||
|
|
@ -98,9 +97,7 @@ SpefReader::SpefReader(const std::string &filename,
|
|||
parasitics->setCouplingCapFactor(coupling_cap_factor);
|
||||
}
|
||||
|
||||
SpefReader::~SpefReader()
|
||||
{
|
||||
}
|
||||
SpefReader::~SpefReader() {}
|
||||
|
||||
bool
|
||||
SpefReader::read()
|
||||
|
|
@ -112,13 +109,13 @@ SpefReader::read()
|
|||
SpefScanner scanner(&stream, filename_, this, report_);
|
||||
scanner_ = &scanner;
|
||||
SpefParse parser(&scanner, this);
|
||||
//parser.set_debug_level(1);
|
||||
// yyparse returns 0 on success.
|
||||
// parser.set_debug_level(1);
|
||||
// yyparse returns 0 on success.
|
||||
success = (parser.parse() == 0);
|
||||
stats.report("Read spef");
|
||||
}
|
||||
else
|
||||
throw FileNotReadable(filename_.c_str());
|
||||
throw FileNotReadable(filename_);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
@ -138,12 +135,9 @@ void
|
|||
SpefReader::setBusBrackets(char left,
|
||||
char right)
|
||||
{
|
||||
if (!((left == '[' && right == ']')
|
||||
|| (left == '{' && right == '}')
|
||||
|| (left == '(' && right == ')')
|
||||
|| (left == '<' && right == '>')
|
||||
|| (left == ':' && right == '\0')
|
||||
|| (left == '.' && right == '\0')))
|
||||
if (!((left == '[' && right == ']') || (left == '{' && right == '}')
|
||||
|| (left == '(' && right == ')') || (left == '<' && right == '>')
|
||||
|| (left == ':' && right == '\0') || (left == '.' && right == '\0')))
|
||||
warn(1640, "illegal bus delimiters.");
|
||||
bus_brkt_left_ = left;
|
||||
bus_brkt_right_ = right;
|
||||
|
|
@ -181,19 +175,16 @@ SpefReader::findPortPinRelative(const char *name)
|
|||
char *
|
||||
SpefReader::translated(const char *token)
|
||||
{
|
||||
return spefToSta(token, divider_, network_->pathDivider(),
|
||||
network_->pathEscape());
|
||||
return spefToSta(token, divider_, network_->pathDivider(), network_->pathEscape());
|
||||
}
|
||||
|
||||
void
|
||||
SpefReader::warn(int id, const char *fmt, ...)
|
||||
int
|
||||
SpefReader::warnLine() const
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
report_->vfileWarn(id, filename_.c_str(), scanner_->line(), fmt, args);
|
||||
va_end(args);
|
||||
return scanner_->line();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SpefReader::setTimeScale(float scale,
|
||||
const char *units)
|
||||
|
|
@ -203,7 +194,7 @@ SpefReader::setTimeScale(float scale,
|
|||
else if (stringEq(units, "PS"))
|
||||
time_scale_ = scale * 1E-12F;
|
||||
else
|
||||
warn(1641, "unknown units %s.", units);
|
||||
warn(1641, "unknown units {}.", units);
|
||||
stringDelete(units);
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +207,7 @@ SpefReader::setCapScale(float scale,
|
|||
else if (stringEq(units, "FF"))
|
||||
cap_scale_ = scale * 1E-15F;
|
||||
else
|
||||
warn(1642, "unknown units %s.", units);
|
||||
warn(1642, "unknown units {}.", units);
|
||||
stringDelete(units);
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +220,7 @@ SpefReader::setResScale(float scale,
|
|||
else if (stringEq(units, "KOHM"))
|
||||
res_scale_ = scale * 1E+3F;
|
||||
else
|
||||
warn(1643, "unknown units %s.", units);
|
||||
warn(1643, "unknown units {}.", units);
|
||||
stringDelete(units);
|
||||
}
|
||||
|
||||
|
|
@ -244,7 +235,7 @@ SpefReader::setInductScale(float scale,
|
|||
else if (stringEq(units, "UH"))
|
||||
induct_scale_ = scale * 1E-6F;
|
||||
else
|
||||
warn(1644, "unknown units %s.", units);
|
||||
warn(1644, "unknown units {}.", units);
|
||||
stringDelete(units);
|
||||
}
|
||||
|
||||
|
|
@ -267,7 +258,7 @@ SpefReader::nameMapLookup(const char *name)
|
|||
if (itr != name_map_.end())
|
||||
return itr->second.c_str();
|
||||
else {
|
||||
warn(1645, "no name map entry for %d.", index);
|
||||
warn(1645, "no name map entry for {}.", index);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -286,7 +277,7 @@ SpefReader::portDirection(char *spef_dir)
|
|||
else if (stringEq(spef_dir, "B"))
|
||||
direction = PortDirection::bidirect();
|
||||
else
|
||||
warn(1646, "unknown port direction %s.", spef_dir);
|
||||
warn(1646, "unknown port direction {}.", spef_dir);
|
||||
return direction;
|
||||
}
|
||||
|
||||
|
|
@ -314,16 +305,16 @@ SpefReader::findPin(char *name)
|
|||
if (inst) {
|
||||
pin = network_->findPin(inst, port_name);
|
||||
if (pin == nullptr)
|
||||
warn(1647, "pin %s not found.", name1);
|
||||
warn(1647, "pin {} not found.", name1);
|
||||
}
|
||||
else
|
||||
warn(1648, "instance %s not found.", name1);
|
||||
warn(1648, "instance {} not found.", name1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pin = findPortPinRelative(name);
|
||||
if (pin == nullptr)
|
||||
warn(1649, "pin %s not found.", name);
|
||||
warn(1649, "pin {} not found.", name);
|
||||
}
|
||||
}
|
||||
return pin;
|
||||
|
|
@ -337,7 +328,7 @@ SpefReader::findNet(const char *name)
|
|||
if (name1) {
|
||||
net = findNetRelative(name1);
|
||||
if (net == nullptr)
|
||||
warn(1650, "net %s not found.", name1);
|
||||
warn(1650, "net {} not found.", name1);
|
||||
}
|
||||
return net;
|
||||
}
|
||||
|
|
@ -366,9 +357,7 @@ SpefReader::rspfDrvrBegin(Pin *drvr_pin,
|
|||
float rpi = pi->r1()->value(triple_index_) * res_scale_;
|
||||
float c1 = pi->c1()->value(triple_index_) * cap_scale_;
|
||||
// Only one parasitic, save it under rise transition.
|
||||
parasitic_ = parasitics_->makePiElmore(drvr_pin,
|
||||
RiseFall::rise(),
|
||||
MinMax::max(),
|
||||
parasitic_ = parasitics_->makePiElmore(drvr_pin, RiseFall::rise(), MinMax::max(),
|
||||
c2, rpi, c1);
|
||||
}
|
||||
delete pi;
|
||||
|
|
@ -412,8 +401,8 @@ SpefReader::dspfBegin(Net *net,
|
|||
delete term_iter;
|
||||
parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner);
|
||||
if (parasitic_ == nullptr)
|
||||
parasitic_ = parasitics_->makeParasiticNetwork(parasitic_owner,
|
||||
pin_cap_included_);
|
||||
parasitic_ =
|
||||
parasitics_->makeParasiticNetwork(parasitic_owner, pin_cap_included_);
|
||||
}
|
||||
net_ = net;
|
||||
}
|
||||
|
|
@ -451,16 +440,15 @@ SpefReader::findParasiticNode(char *name,
|
|||
// <instance>:<port>
|
||||
Pin *pin = network_->findPin(inst, name2);
|
||||
if (pin) {
|
||||
if (local_only
|
||||
&& !network_->isConnected(net_, pin))
|
||||
warn(1651, "%s not connected to net %s.",
|
||||
name1, sdc_network_->pathName(net_));
|
||||
if (local_only && !network_->isConnected(net_, pin))
|
||||
warn(1651, "{} not connected to net {}.", name1,
|
||||
sdc_network_->pathName(net_));
|
||||
return parasitics_->ensureParasiticNode(parasitic_, pin, network_);
|
||||
}
|
||||
else {
|
||||
// Replace delimiter for error message.
|
||||
*delim = delimiter_;
|
||||
warn(1652, "pin %s not found.", name1);
|
||||
warn(1652, "pin {} not found.", name1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -472,15 +460,13 @@ SpefReader::findParasiticNode(char *name,
|
|||
const char *id_str = delim + 1;
|
||||
if (isDigits(id_str)) {
|
||||
int id = atoi(id_str);
|
||||
if (local_only
|
||||
&& !network_->isConnected(net, net_))
|
||||
warn(1653, "%s not connected to net %s.",
|
||||
name1,
|
||||
if (local_only && !network_->isConnected(net, net_))
|
||||
warn(1653, "{} not connected to net {}.", name1,
|
||||
network_->pathName(net_));
|
||||
return parasitics_->ensureParasiticNode(parasitic_, net, id, network_);
|
||||
}
|
||||
else
|
||||
warn(1654, "node %s not a pin or net:number", name1);
|
||||
warn(1654, "node {} not a pin or net:number", name1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -491,23 +477,24 @@ SpefReader::findParasiticNode(char *name,
|
|||
if (name1) {
|
||||
Pin *pin = findPortPinRelative(name1);
|
||||
if (pin) {
|
||||
if (local_only
|
||||
&& !network_->isConnected(net_, pin))
|
||||
warn(1655, "%s not connected to net %s.", name1, network_->pathName(net_));
|
||||
if (local_only && !network_->isConnected(net_, pin))
|
||||
warn(1655, "{} not connected to net {}.", name1,
|
||||
network_->pathName(net_));
|
||||
return parasitics_->ensureParasiticNode(parasitic_, pin, network_);
|
||||
}
|
||||
else
|
||||
warn(1656, "pin %s not found.", name1);
|
||||
warn(1656, "pin {} not found.", name1);
|
||||
}
|
||||
else
|
||||
warn(1657, "pin %s not found.", name);
|
||||
warn(1657, "pin {} not found.", name);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
SpefReader::makeCapacitor(int, char *node_name,
|
||||
SpefReader::makeCapacitor(int,
|
||||
char *node_name,
|
||||
SpefTriple *cap)
|
||||
{
|
||||
ParasiticNode *node = findParasiticNode(node_name, true);
|
||||
|
|
@ -622,7 +609,7 @@ SpefScanner::SpefScanner(std::istream *stream,
|
|||
void
|
||||
SpefScanner::error(const char *msg)
|
||||
{
|
||||
report_->fileError(1867, filename_.c_str(), lineno(), "%s", msg);
|
||||
report_->fileError(1658, filename_.c_str(), lineno(), "{}", msg);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace sta
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue