use std::format squash

This commit is contained in:
James Cherry 2026-03-15 14:35:24 -07:00
parent 5fbd2a18b5
commit 134b547501
129 changed files with 5898 additions and 6990 deletions

View File

@ -403,6 +403,17 @@ find_package(Threads)
find_package(Eigen3 REQUIRED)
# 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()
include(cmake/FindCUDD.cmake)
# configure a header file to pass some of the CMake settings
@ -518,6 +529,7 @@ target_sources(OpenSTA
target_link_libraries(OpenSTA
Eigen3::Eigen
fmt::fmt
${TCL_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
${CUDD_LIB}

View File

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

View File

@ -60,7 +60,7 @@ 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());
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.

View File

@ -94,24 +94,24 @@ makeArcDcalcArg(const char *inst_name,
else {
const Network *network = sta->network();
const Instance *inst = network->instance(in_pin);
report->warn(2100, "no timing arc for %s input/driver pins.",
report->warn(2100, "no timing arc for {} input/driver pins.",
network->pathName(inst));
}
}
else
report->warn(2101, "%s not a valid rise/fall.", drvr_rf_name);
report->warn(2101, "{} not a valid rise/fall.", drvr_rf_name);
}
else
report->warn(2102, "Pin %s/%s not found.", inst_name, drvr_port_name);
report->warn(2102, "Pin {}/{} not found.", inst_name, drvr_port_name);
}
else
report->warn(2103, "%s not a valid rise/fall.", in_rf_name);
report->warn(2103, "{} not a valid rise/fall.", in_rf_name);
}
else
report->warn(2104, "Pin %s/%s not found.", inst_name, in_port_name);
report->warn(2104, "Pin {}/{} not found.", inst_name, in_port_name);
}
else
report->warn(2105, "Instance %s not found.", inst_name);
report->warn(2105, "Instance {} not found.", inst_name);
return ArcDcalcArg();
}

View File

@ -1156,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;
}
@ -1290,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));
@ -1399,7 +1399,7 @@ ArnoldiDelayCalc::ar1_ceff_delay(delay_work *D,
double ceff,tlohi,t50_sy,r,s,t50_sr,rdelay;
float df, sf;
debugPrint(debug_, "arnoldi", 1, "ctot=%s",
debugPrint(debug_, "arnoldi", 1, "ctot={}",
units_->capacitanceUnit()->asString(ctot));
rdelay = ra_rdelay_1(tab,ctot);
@ -1421,18 +1421,18 @@ 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, df, sf);
debugPrint(debug_, "arnoldi", 1, "table slew (in_slew %s ctot %s) = %s",
debugPrint(debug_, "arnoldi", 1, "table slew (in_slew {} ctot {}) = {}",
units_->timeUnit()->asString(tab->in_slew),
units_->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));
}
@ -1468,11 +1468,11 @@ 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),

View File

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

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "CcsCeffDelayCalc.hh"
@ -63,10 +63,7 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
{
}
CcsCeffDelayCalc::~CcsCeffDelayCalc()
{
delete table_dcalc_;
}
CcsCeffDelayCalc::~CcsCeffDelayCalc() { delete table_dcalc_; }
ArcDelayCalc *
CcsCeffDelayCalc::copy()
@ -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,7 +104,7 @@ 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_;
@ -115,12 +112,12 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin,
drvr_cell->ensureVoltageWaveforms(scenes_);
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());
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,
@ -140,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 = 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++) {
@ -172,15 +169,14 @@ 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(drvr_slew - prev_drvr_slew) < .01 * prev_drvr_slew)
@ -215,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_);
}
@ -285,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)
@ -312,10 +308,8 @@ CcsCeffDelayCalc::makeResult(const LibertyLibrary *drvr_library,
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);
@ -342,8 +336,7 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
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 &&
@ -371,7 +364,7 @@ CcsCeffDelayCalc::loadDelaySlew(const Pin *load_pin,
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;
@ -387,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));
}
@ -455,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;
}
@ -485,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;
}
@ -521,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;
@ -553,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;
@ -576,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++) {
@ -598,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;
@ -628,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) {
@ -643,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;
}
}
@ -669,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;
}
@ -684,7 +674,7 @@ CcsCeffDelayCalc::fail(const char *reason)
{
// Report failures with a unique debug flag.
if (debug_->check("ccs_dcalc", 1) || debug_->check("dcalc_error", 1))
report_->reportLine("delay_calc: CCS failed - %s", reason);
report_->report("delay_calc: CCS failed - {}", reason);
}
} // namespace
} // namespace sta

View File

@ -215,7 +215,7 @@ delayDblAsDelay(DelayDbl &delay)
return Delay(delay.mean(), delay.meanShift(), delay.stdDev2(), delay.skewness());
}
const char *
std::string
delayAsString(const Delay &delay,
const StaState *sta)
{
@ -223,7 +223,7 @@ delayAsString(const Delay &delay,
sta->units()->timeUnit()->digits(), sta);
}
const char *
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta)
@ -231,7 +231,7 @@ delayAsString(const Delay &delay,
return delayAsString(delay, early_late, sta->units()->timeUnit()->digits(), sta);
}
const char *
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
int digits,
@ -242,7 +242,7 @@ delayAsString(const Delay &delay,
return unit->asString(mean_std_dev, digits);
}
const char *
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
bool report_variance,

View File

@ -131,7 +131,7 @@ 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."
}
}
@ -154,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]
@ -229,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."
}
}
@ -250,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)] } {
@ -259,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]
@ -281,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)] } {
@ -289,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]
@ -341,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."
}
}
@ -362,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]]

View File

@ -123,6 +123,7 @@ DelayCalcBase::thresholdAdjust(const Pin *load_pin,
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();

View File

@ -29,6 +29,7 @@
#include "Error.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "Format.hh"
#include "StaState.hh"
#include "Variables.hh"
@ -217,15 +218,15 @@ DelayOpsNormal::div(float delay1,
return Delay(delay1 / delay2.mean());
}
const char *
std::string
DelayOpsNormal::asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const
{
const Unit *unit = sta->units()->timeUnit();
return stringPrintTmp("%s[%s]",
unit->asString(delay.mean(), digits),
unit->asString(delay.stdDev(), digits));
return sta::format("{}[{}]",
unit->asString(delay.mean(), digits),
unit->asString(delay.stdDev(), digits));
}
} // namespace

View File

@ -193,7 +193,7 @@ DelayOpsScalar::div(float delay1,
return Delay(delay1 / delay2.mean());
}
const char *
std::string
DelayOpsScalar::asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const

View File

@ -29,6 +29,7 @@
#include "Error.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "Format.hh"
#include "StaState.hh"
#include "Variables.hh"
@ -276,17 +277,17 @@ DelayOpsSkewNormal::div(float delay1,
return Delay(delay1 / delay2.mean());
}
const char *
std::string
DelayOpsSkewNormal::asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const
{
const Unit *unit = sta->units()->timeUnit();
return stringPrintTmp("%s[%s,%s,%s]",
unit->asString(delay.mean(), digits),
unit->asString(delay.meanShift(), digits),
unit->asString(delay.stdDev(), digits),
sta->units()->scalarUnit()->asString(delay.skewness(), digits));
return sta::format("{}[{},{},{}]",
unit->asString(delay.mean(), digits),
unit->asString(delay.meanShift(), digits),
unit->asString(delay.stdDev(), digits),
sta->units()->scalarUnit()->asString(delay.skewness(), digits));
}
} // namespace

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
// "Performance Computation for Precharacterized CMOS Gates with RC Loads",
@ -36,6 +36,7 @@
#include <cmath>
#include <functional>
#include "Format.hh"
#include "Report.hh"
#include "Debug.hh"
#include "Units.hh"
@ -97,7 +98,7 @@ newtonRaphson(const int max_iter,
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,
@ -122,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.
@ -136,9 +138,9 @@ 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.
@ -188,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);
@ -260,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.
@ -274,7 +276,7 @@ protected:
};
DmpAlg::DmpAlg(int nr_order,
StaState *sta):
StaState *sta) :
StaState(sta),
c2_(0.0),
rpi_(0.0),
@ -328,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();
@ -409,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;
}
}
@ -433,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
@ -448,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);
@ -478,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;
@ -510,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;
@ -527,12 +525,12 @@ 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);
}
}
@ -581,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_;
@ -596,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;
@ -650,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);
}
}
@ -664,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_));
}
////////////////////////////////////////////////////////////////
@ -690,9 +684,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 loadDelaySlew(const Pin *,
double elmore,
// Return values.
@ -712,8 +706,9 @@ private:
double &dvl_dt) override;
};
DmpCap::DmpCap(StaState *sta):
DmpAlg(1, sta)
DmpCap::DmpCap(StaState *sta) :
DmpAlg(1,
sta)
{
}
@ -730,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;
@ -778,7 +773,7 @@ DmpCap::voCrossingUpperBound()
}
void
DmpCap::Vl0(double ,
DmpCap::Vl0(double,
// Return values.
double &vl,
double &dvl_dt)
@ -805,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;
@ -843,7 +838,8 @@ private:
};
DmpPi::DmpPi(StaState *sta) :
DmpAlg(3, sta),
DmpAlg(3,
sta),
p1_(0.0),
p2_(0.0),
z1_(0.0),
@ -871,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_);
@ -896,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 {
@ -907,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_;
@ -937,8 +931,7 @@ DmpPi::findDriverParamsPi()
{
try {
findDriverParams(c2_ + c1_);
}
catch (DmpError &) {
} catch (DmpError &) {
findDriverParams(c2_);
}
}
@ -981,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)
{
@ -1018,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;
}
@ -1047,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
@ -1076,7 +1065,8 @@ public:
};
DmpOnePole::DmpOnePole(StaState *sta) :
DmpAlg(2, sta)
DmpAlg(2,
sta)
{
}
@ -1100,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(".................");
}
}
@ -1140,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.
@ -1189,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_);
@ -1203,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_);
@ -1213,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;
@ -1237,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_);
@ -1267,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,
@ -1529,12 +1514,13 @@ DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
}
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;
@ -1570,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
@ -1593,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;
@ -1653,9 +1639,9 @@ gateModelRd(const LibertyCell *cell,
}
void
DmpCeffDelayCalc::gateDelaySlew(// Return values.
double &delay,
double &slew)
DmpCeffDelayCalc::gateDelaySlew( // Return values.
double &delay,
double &slew)
{
dmp_alg_->gateDelaySlew(delay, slew);
}
@ -1683,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.
@ -1712,4 +1698,4 @@ exp2(double x)
}
}
} // namespace
} // namespace sta

View File

@ -251,8 +251,8 @@ GraphDelayCalc::delayInvalid(const Pin *pin)
void
GraphDelayCalc::delayInvalid(Vertex *vertex)
{
debugPrint(debug_, "delay_calc", 2, "delay invalid %s",
vertex->to_string(this).c_str());
debugPrint(debug_, "delay_calc", 2, "delay invalid {}",
vertex->to_string(this));
if (graph_ && incremental_) {
invalid_delays_.insert(vertex);
// Invalidate driver that triggers dcalc for multi-driver nets.
@ -340,7 +340,7 @@ GraphDelayCalc::findDelays(Level level)
if (arc_delay_calc_) {
Stats stats(debug_, report_);
int dcalc_count = 0;
debugPrint(debug_, "delay_calc", 1, "find delays to level %d", level);
debugPrint(debug_, "delay_calc", 1, "find delays to level {}", level);
if (!delays_seeded_) {
iter_->clear();
seedRootSlews();
@ -368,7 +368,7 @@ GraphDelayCalc::findDelays(Level level)
delays_exist_ = true;
incremental_ = true;
debugPrint(debug_, "delay_calc", 1, "found %d delays", dcalc_count);
debugPrint(debug_, "delay_calc", 1, "found {} delays", dcalc_count);
stats.report("Delay calc");
}
}
@ -404,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()) {
@ -527,8 +527,8 @@ 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();
@ -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);
@ -658,7 +658,7 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin,
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);
@ -885,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);
@ -977,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);
@ -1200,16 +1200,16 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge,
{
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;
@ -1259,8 +1259,8 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex,
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;
@ -1580,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,16 +1602,16 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge,
scene, min_max);
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;
@ -1622,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;

View File

@ -133,7 +133,7 @@ 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();

View File

@ -1,30 +1,30 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "PrimaDelayCalc.hh"
#include <cmath> // abs
#include <cmath> // abs
#include "Debug.hh"
#include "Units.hh"
@ -38,15 +38,16 @@
#include "Parasitics.hh"
#include "GraphDelayCalc.hh"
#include "DmpDelayCalc.hh"
#include "Format.hh"
#include <Eigen/LU>
#include <Eigen/QR>
namespace sta {
using Eigen::SparseLU;
using Eigen::HouseholderQR;
using Eigen::ColPivHouseholderQR;
using Eigen::HouseholderQR;
using Eigen::SparseLU;
// Lawrence Pillage - “Electronic Circuit & System Simulation Methods” 1998
// McGraw-Hill, Inc. New York, NY.
@ -90,10 +91,7 @@ PrimaDelayCalc::PrimaDelayCalc(const PrimaDelayCalc &dcalc) :
{
}
PrimaDelayCalc::~PrimaDelayCalc()
{
delete table_dcalc_;
}
PrimaDelayCalc::~PrimaDelayCalc() { delete table_dcalc_; }
ArcDelayCalc *
PrimaDelayCalc::copy()
@ -130,8 +128,8 @@ PrimaDelayCalc::findParasitic(const Pin *drvr_pin,
bool has_wire_cap;
graph_delay_calc_->netCaps(drvr_pin, rf, scene, min_max, pin_cap, wire_cap,
fanout, has_wire_cap);
parasitic = parasitics->makeWireloadNetwork(drvr_pin, wireload,
fanout, scene, min_max);
parasitic =
parasitics->makeWireloadNetwork(drvr_pin, wireload, fanout, scene, min_max);
}
return parasitic;
}
@ -160,8 +158,8 @@ 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;
@ -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;
}
@ -726,10 +712,8 @@ PrimaDelayCalc::dcalcResults()
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());
@ -741,8 +725,7 @@ PrimaDelayCalc::dcalcResults()
ThresholdTimes &drvr_times = threshold_times_[drvr_node];
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 %s %s delay %s slew %s",
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_);
@ -957,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;
}
@ -969,7 +954,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name,
MatrixSd &matrix)
{
report_->reportLine("%s", name);
report_->report("{}", name);
reportMatrix(matrix);
}
@ -977,7 +962,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name,
Eigen::MatrixXd &matrix)
{
report_->reportLine("%s", name);
report_->report("{}", name);
reportMatrix(matrix);
}
@ -985,7 +970,7 @@ void
PrimaDelayCalc::reportMatrix(const char *name,
Eigen::VectorXd &matrix)
{
report_->reportLine("%s", name);
report_->report("{}", name);
reportMatrix(matrix);
}
@ -993,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);
}
}
@ -1017,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);
}
}
@ -1032,25 +1011,21 @@ PrimaDelayCalc::reportMatrix(Eigen::VectorXd &matrix)
{
std::string line = "| ";
for (Eigen::Index i = 0; i < matrix.rows(); i++) {
std::string entry = stdstrPrint("%10.3e", matrix.coeff(i));
line += entry;
line += " ";
std::string entry =
line += sta::format("{:10.3e}", matrix.coeff(i)) + " ";
}
line += "|";
report_->reportLineString(line);
report_->reportLine(line);
}
void
PrimaDelayCalc::reportVector(std::vector<double> &matrix)
{
std::string line = "| ";
for (size_t i = 0; i < matrix.size(); i++) {
std::string entry = stdstrPrint("%10.3e", matrix[i]);
line += entry;
line += " ";
}
for (size_t i = 0; i < matrix.size(); i++)
line += sta::format("{:10.3e}", matrix[i]) + " ";
line += "|";
report_->reportLineString(line);
report_->reportLine(line);
}
} // namespace
} // namespace sta

View File

@ -24,6 +24,19 @@
This file summarizes STA API changes for each release.
2026/03/12
----------
The Report class used for reporting and error messages now uses std::format
instead of printf.
sta::format is a wrapper for std::format that will compile on gcc8, which
centos7 uses and does not support std::format.
stdstrPrint, strintPrint, stringAppend have been removed. Use sta::format.
reportLineString is now reportLine
Release 3.0.0 2025/01/03
------------------------

View File

@ -2,6 +2,7 @@ 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
------------------------

View File

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

View File

@ -283,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;

View File

@ -292,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()) {
@ -308,9 +308,7 @@ latch_d_to_q_en()
const RiseFall *enable_rf;
lib_cell->latchEnable(d_q_set, enable_port, enable_func, enable_rf);
if (enable_port)
return stringPrintTmp("%s %s",
enable_port->name(),
enable_rf->shortName());
return sta::format("{} {}", enable_port->name(), enable_rf->shortName());
}
return "";
}

View File

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

View File

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

View File

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

View File

@ -155,7 +155,7 @@ public:
float delay2) const = 0;
virtual Delay div(float delay1,
const Delay &delay2) const = 0;
virtual const char *asStringVariance(const Delay &delay,
virtual std::string asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const = 0;
@ -203,19 +203,19 @@ void
delaySetMean(Delay &delay,
float mean);
const char *
std::string
delayAsString(const Delay &delay,
const StaState *sta);
const char *
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
const StaState *sta);
const char *
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
int digits,
const StaState *sta);
const char *
std::string
delayAsString(const Delay &delay,
const EarlyLate *early_late,
bool report_variance,

View File

@ -82,7 +82,7 @@ public:
float delay2) const override;
Delay div(float delay1,
const Delay &delay2) const override;
const char *asStringVariance(const Delay &delay,
std::string asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const override;
};

View File

@ -82,7 +82,7 @@ public:
float delay2) const override;
Delay div(float delay1,
const Delay &delay2) const override;
const char *asStringVariance(const Delay &delay,
std::string asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const override;
};

View File

@ -82,7 +82,7 @@ public:
float delay2) const override;
Delay div(float delay1,
const Delay &delay2) const override;
const char *asStringVariance(const Delay &delay,
std::string asStringVariance(const Delay &delay,
int digits,
const StaState *sta) const override;

View File

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

View File

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

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

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

View File

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

View File

@ -42,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.
@ -140,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;
@ -191,7 +191,7 @@ protected:
bool gated_clk,
bool unconstrained,
const MinMax *min_max);
bool reportGroup(const char *group_name,
bool reportGroup(const std::string &group_name,
StringSet &group_names) const;
static GroupPath *groupPathTo(const PathEnd *path_end,
const StaState *sta);

View File

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

View File

@ -44,20 +44,20 @@ class ReportTcl : public Report
public:
ReportTcl();
virtual ~ReportTcl();
virtual void logBegin(const char *filename);
virtual void logEnd();
virtual void redirectFileBegin(const char *filename);
virtual void redirectFileAppendBegin(const char *filename);
virtual void redirectFileEnd();
virtual void redirectStringBegin();
virtual const char *redirectStringEnd();
void logBegin(std::string_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:

View File

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

View File

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

View File

@ -434,19 +434,21 @@ public:
const RiseFallBoth *to_rf,
const SetupHoldAll *setup_hold,
Sdc *sdc);
ClockGroups *makeClockGroups(const char *name,
ClockGroups *makeClockGroups(const std::string &name,
bool logically_exclusive,
bool physically_exclusive,
bool asynchronous,
bool allow_paths,
const char *comment,
Sdc *sdc);
// nullptr name removes all.
void removeClockGroupsLogicallyExclusive(const char *name,
void removeClockGroupsLogicallyExclusive(Sdc *sdc);
void removeClockGroupsLogicallyExclusive(const std::string &name,
Sdc *sdc);
void removeClockGroupsPhysicallyExclusive(const char *name,
void removeClockGroupsPhysicallyExclusive(Sdc *sdc);
void removeClockGroupsPhysicallyExclusive(const std::string &name,
Sdc *sdc);
void removeClockGroupsAsynchronous(const char *name,
void removeClockGroupsAsynchronous(Sdc *sdc);
void removeClockGroupsAsynchronous(const std::string &name,
Sdc *sdc);
void makeClockGroup(ClockGroups *clk_groups,
ClockSet *clks,
@ -640,7 +642,7 @@ public:
float delay,
const char *comment,
Sdc *sdc);
void makeGroupPath(const char *name,
void makeGroupPath(const std::string &name,
bool is_default,
ExceptionFrom *from,
ExceptionThruSeq *thrus,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@
#include "Liberty.hh"
#include "ContainerHelpers.hh"
#include "Format.hh"
#include "Mutex.hh"
#include "EnumNameMap.hh"
#include "Report.hh"
@ -775,7 +776,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1,
port1->setScenePort(port2, ap_index);
}
else
report->warn(1110, "cell %s/%s port %s not found in cell %s/%s.",
report->warn(1110, "cell {}/{} port {} not found in cell {}/{}.",
cell1->library()->name(),
cell1->name(),
port_name,
@ -801,7 +802,7 @@ LibertyLibrary::makeSceneMap(LibertyCell *cell1,
}
}
else
report->warn(1111, "cell %s/%s %s -> %s timing group %s not found in cell %s/%s.",
report->warn(1111, "cell {}/{} {} -> {} timing group {} not found in cell {}/{}.",
cell1->library()->name(),
cell1->name(),
arc_set1->from() ? arc_set1->from()->name() : "",
@ -820,7 +821,7 @@ LibertyLibrary::checkScenes(LibertyCell *cell,
for (const Scene *scene : scenes) {
for (auto min_max : MinMax::range()) {
if (!cell->checkSceneCell(scene, min_max))
report->error(1112, "Liberty cell %s/%s for corner %s/%s not found.",
report->error(1112, "Liberty cell {}/{} for corner {}/{} not found.",
cell->libertyLibrary()->name(),
cell->name(),
scene->name().c_str(),
@ -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);
}
}

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
#include <functional>
#include <memory>
#include <array>
#include <string_view>
#include <vector>
#include <unordered_map>
@ -44,6 +45,7 @@
#include "LibertyParser.hh"
#include "LibertyReader.hh"
#include "LibertyBuilder.hh"
#include "Report.hh"
namespace sta {
@ -451,38 +453,65 @@ protected:
const char *attr_name,
const LibertyCell *cell,
int line);
void libWarn(int id,
template <typename... Args>
void warn(int id,
const LibertyGroup *group,
const char *fmt,
...) const
__attribute__((format (printf, 4, 5)));
void libWarn(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileWarn(id, filename_, group->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
const LibertySimpleAttr *attr,
const char *fmt,
...) const
__attribute__((format (printf, 4, 5)));
void libWarn(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileWarn(id, filename_, attr->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
const LibertyComplexAttr *attr,
const char *fmt,
...) const
__attribute__((format (printf, 4, 5)));
void libWarn(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileWarn(id, filename_, attr->line(), fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void warn(int id,
int line,
const char *fmt,
...) const
__attribute__((format (printf, 4, 5)));
void libError(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileWarn(id, filename_, line, fmt, std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertyGroup *group,
const char *fmt, ...) const
__attribute__((format (printf, 4, 5)));
void libError(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileError(id, filename_, group->line(), fmt,
std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertySimpleAttr *attr,
const char *fmt, ...) const
__attribute__((format (printf, 4, 5)));
void libError(int id,
std::string_view fmt,
Args &&...args) const
{
report_->fileError(id, filename_, attr->line(), fmt,
std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
const LibertyComplexAttr *attr,
const char *fmt, ...) const
__attribute__((format (printf, 4, 5)));
std::string_view fmt,
Args &&...args) const
{
report_->fileError(id, filename_, attr->line(), fmt,
std::forward<Args>(args)...);
}
const char *filename_;
bool infer_latches_;

View File

@ -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,54 +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->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;
}
}
@ -509,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
@ -534,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 *
@ -572,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";
}
@ -595,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";
@ -612,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";
@ -649,13 +635,11 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
else if (role == TimingRole::width())
return "min_pulse_width";
else {
report_->error(1343, "%s/%s/%s timing arc type %s not supported.",
library_->name(),
arc_set->to()->libertyCell()->name(),
arc_set->to()->name(),
role->to_string().c_str());
report_->error(1343, "{}/{}/{} timing arc type {} not supported.",
library_->name(), arc_set->to()->libertyCell()->name(),
arc_set->to()->name(), role->to_string());
return nullptr;
}
}
} // namespace
} // namespace sta

View File

@ -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 "TableModel.hh"
@ -250,12 +250,12 @@ GateTableModel::reportTableLookup(const char *result_name,
{
if (model) {
float axis_value1, axis_value2, axis_value3;
findAxisValues(model, in_slew, load_cap, related_out_cap,
axis_value1, axis_value2, axis_value3);
findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1,
axis_value2, axis_value3);
const LibertyLibrary *library = cell_->libertyLibrary();
return model->reportValue(result_name, cell_, pvt, axis_value1, nullptr,
axis_value2, axis_value3,
library->units()->timeUnit(), digits);
axis_value2, axis_value3, library->units()->timeUnit(),
digits);
}
return "";
}
@ -269,8 +269,8 @@ GateTableModel::findValue(const Pvt *pvt,
{
if (model) {
float axis_value1, axis_value2, axis_value3;
findAxisValues(model, in_slew, load_cap, related_out_cap,
axis_value1, axis_value2, axis_value3);
findAxisValues(model, in_slew, load_cap, related_out_cap, axis_value1,
axis_value2, axis_value3);
return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3);
}
else
@ -288,37 +288,31 @@ GateTableModel::findAxisValues(const TableModel *model,
float &axis_value3) const
{
switch (model->order()) {
case 0:
axis_value1 = 0.0;
axis_value2 = 0.0;
axis_value3 = 0.0;
break;
case 1:
axis_value1 = axisValue(model->axis1(), in_slew, load_cap,
related_out_cap);
axis_value2 = 0.0;
axis_value3 = 0.0;
break;
case 2:
axis_value1 = axisValue(model->axis1(), in_slew, load_cap,
related_out_cap);
axis_value2 = axisValue(model->axis2(), in_slew, load_cap,
related_out_cap);
axis_value3 = 0.0;
break;
case 3:
axis_value1 = axisValue(model->axis1(), in_slew, load_cap,
related_out_cap);
axis_value2 = axisValue(model->axis2(), in_slew, load_cap,
related_out_cap);
axis_value3 = axisValue(model->axis3(), in_slew, load_cap,
related_out_cap);
break;
default:
axis_value1 = 0.0;
axis_value2 = 0.0;
axis_value3 = 0.0;
criticalError(239, "unsupported table order");
case 0:
axis_value1 = 0.0;
axis_value2 = 0.0;
axis_value3 = 0.0;
break;
case 1:
axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap);
axis_value2 = 0.0;
axis_value3 = 0.0;
break;
case 2:
axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap);
axis_value2 = axisValue(model->axis2(), in_slew, load_cap, related_out_cap);
axis_value3 = 0.0;
break;
case 3:
axis_value1 = axisValue(model->axis1(), in_slew, load_cap, related_out_cap);
axis_value2 = axisValue(model->axis2(), in_slew, load_cap, related_out_cap);
axis_value3 = axisValue(model->axis3(), in_slew, load_cap, related_out_cap);
break;
default:
axis_value1 = 0.0;
axis_value2 = 0.0;
axis_value3 = 0.0;
criticalError(239, "unsupported table order");
}
}
@ -348,12 +342,12 @@ GateTableModel::maxCapSlew(float in_slew,
slew = findValue(pvt, model, in_slew, cap, 0.0);
}
else if (axis2
&& axis2->variable()==TableAxisVariable::total_output_net_capacitance) {
&& axis2->variable() == TableAxisVariable::total_output_net_capacitance) {
cap = axis2->axisValue(axis2->size() - 1);
slew = findValue(pvt, model, in_slew, cap, 0.0);
}
else if (axis3
&& axis3->variable()==TableAxisVariable::total_output_net_capacitance) {
&& axis3->variable() == TableAxisVariable::total_output_net_capacitance) {
cap = axis3->axisValue(axis3->size() - 1);
slew = findValue(pvt, model, in_slew, cap, 0.0);
}
@ -408,9 +402,9 @@ GateTableModel::checkAxis(const TableAxis *axis)
{
TableAxisVariable var = axis->variable();
return var == TableAxisVariable::total_output_net_capacitance
|| var == TableAxisVariable::input_transition_time
|| var == TableAxisVariable::input_net_transition
|| var == TableAxisVariable::related_out_total_output_net_capacitance;
|| var == TableAxisVariable::input_transition_time
|| var == TableAxisVariable::input_net_transition
|| var == TableAxisVariable::related_out_total_output_net_capacitance;
}
////////////////////////////////////////////////////////////////
@ -435,12 +429,13 @@ ReceiverModel::checkAxes(const TableModel *table)
const TableAxis *axis2 = table->axis2();
const TableAxis *axis3 = table->axis3();
return (axis1 && axis1->variable() == TableAxisVariable::input_net_transition
&& axis2 == nullptr
&& axis2 == nullptr && axis3 == nullptr)
|| (axis1 && axis1->variable() == TableAxisVariable::input_net_transition
&& axis2
&& axis2->variable() == TableAxisVariable::total_output_net_capacitance
&& axis3 == nullptr)
|| (axis1 && axis1->variable() == TableAxisVariable::input_net_transition
&& axis2 && axis2->variable() == TableAxisVariable::total_output_net_capacitance
&& axis3 == nullptr)
|| (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance
|| (axis1
&& axis1->variable() == TableAxisVariable::total_output_net_capacitance
&& axis2 && axis2->variable() == TableAxisVariable::input_net_transition
&& axis3 == nullptr);
}
@ -488,7 +483,8 @@ CheckTableModel::checkDelay(const Pvt *pvt,
if (std_dev_model == nullptr)
std_dev_model = check_models_->sigma(min_max);
if (std_dev_model) {
float std_dev = findValue(pvt, std_dev_model, from_slew, to_slew, related_out_cap);
float std_dev = findValue(pvt, std_dev_model, from_slew,
to_slew, related_out_cap);
check_delay.setStdDev(std_dev);
}
break;
@ -529,8 +525,8 @@ CheckTableModel::findValue(const Pvt *pvt,
{
if (model) {
float axis_value1, axis_value2, axis_value3;
findAxisValues(from_slew, to_slew, related_out_cap,
axis_value1, axis_value2, axis_value3);
findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2,
axis_value3);
return model->findValue(cell_, pvt, axis_value1, axis_value2, axis_value3);
}
else
@ -580,8 +576,8 @@ CheckTableModel::reportTableDelay(const char *result_name,
{
if (model) {
float axis_value1, axis_value2, axis_value3;
findAxisValues(from_slew, to_slew, related_out_cap,
axis_value1, axis_value2, axis_value3);
findAxisValues(from_slew, to_slew, related_out_cap, axis_value1, axis_value2,
axis_value3);
std::string result = reportPvt(cell_, pvt, digits);
const Unit *time_unit = cell_->libertyLibrary()->units()->timeUnit();
result += check_models_->model()->reportValue(result_name, cell_, pvt,
@ -675,8 +671,8 @@ CheckTableModel::checkAxis(const TableAxis *axis)
{
TableAxisVariable var = axis->variable();
return var == TableAxisVariable::constrained_pin_transition
|| var == TableAxisVariable::related_pin_transition
|| var == TableAxisVariable::related_out_total_output_net_capacitance;
|| var == TableAxisVariable::related_pin_transition
|| var == TableAxisVariable::related_out_total_output_net_capacitance;
}
////////////////////////////////////////////////////////////////
@ -837,7 +833,7 @@ TableModel::findValue(const LibertyCell *cell,
float axis_value3) const
{
return table_->findValue(axis_value1, axis_value2, axis_value3)
* scaleFactor(cell, pvt);
* scaleFactor(cell, pvt);
}
float
@ -849,8 +845,8 @@ TableModel::scaleFactor(const LibertyCell *cell,
// nominal pvt.
return 1.0F;
else
return cell->libertyLibrary()->scaleFactor(static_cast<ScaleFactorType>(scale_factor_type_),
rf_index_, cell, pvt);
return cell->libertyLibrary()->scaleFactor(
static_cast<ScaleFactorType>(scale_factor_type_), rf_index_, cell, pvt);
}
std::string
@ -864,14 +860,16 @@ TableModel::reportValue(const char *result_name,
const Unit *table_unit,
int digits) const
{
std::string result = table_->reportValue("Table value", cell, pvt, value1,
comment1, value2, value3, table_unit, digits);
std::string result =
table_->reportValue("Table value", cell, pvt, value1, comment1, value2, value3,
table_unit, digits);
result += reportPvtScaleFactor(cell, pvt, digits);
result += result_name;
result += " = ";
result += table_unit->asString(findValue(cell, pvt, value1, value2, value3), digits);
result +=
table_unit->asString(findValue(cell, pvt, value1, value2, value3), digits);
result += '\n';
return result;
}
@ -884,14 +882,11 @@ reportPvt(const LibertyCell *cell,
const LibertyLibrary *library = cell->libertyLibrary();
if (pvt == nullptr)
pvt = library->defaultOperatingConditions();
if (pvt) {
std::string result;
stringPrint(result, "P = %.*f V = %.*f T = %.*f\n",
digits, pvt->process(),
digits, pvt->voltage(),
digits, pvt->temperature());
return result;
}
if (pvt)
return sta::format("P = {:.{}f} V = {:.{}f} T = {:.{}f}\n",
pvt->process(), digits,
pvt->voltage(), digits,
pvt->temperature(), digits);
return "";
}
@ -902,13 +897,9 @@ TableModel::reportPvtScaleFactor(const LibertyCell *cell,
{
if (pvt == nullptr)
pvt = cell->libertyLibrary()->defaultOperatingConditions();
if (pvt) {
std::string result;
stringPrint(result, "PVT scale factor = %.*f\n",
digits,
scaleFactor(cell, pvt));
return result;
}
if (pvt)
return sta::formatRuntime("PVT scale factor = {:.{}f}\n",
scaleFactor(cell, pvt), digits);
return "";
}
@ -1189,13 +1180,10 @@ Table::findValueOrder3(float axis_value1,
}
return (1 - dx1) * (1 - dx2) * (1 - dx3) * y000
+ (1 - dx1) * (1 - dx2) * dx3 * y001
+ (1 - dx1) * dx2 * (1 - dx3) * y010
+ (1 - dx1) * dx2 * dx3 * y011
+ dx1 * (1 - dx2) * (1 - dx3) * y100
+ dx1 * (1 - dx2) * dx3 * y101
+ dx1 * dx2 * (1 - dx3) * y110
+ dx1 * dx2 * dx3 * y111;
+ (1 - dx1) * (1 - dx2) * dx3 * y001 + (1 - dx1) * dx2 * (1 - dx3) * y010
+ (1 - dx1) * dx2 * dx3 * y011 + dx1 * (1 - dx2) * (1 - dx3) * y100
+ dx1 * (1 - dx2) * dx3 * y101 + dx1 * dx2 * (1 - dx3) * y110
+ dx1 * dx2 * dx3 * y111;
}
void
@ -1279,14 +1267,14 @@ Table::reportValue(const char *result_name,
case 0:
return reportValueOrder0(result_name, comment1, table_unit, digits);
case 1:
return reportValueOrder1(result_name, cell, value1, comment1,
value2, value3, table_unit, digits);
return reportValueOrder1(result_name, cell, value1, comment1, value2, value3,
table_unit, digits);
case 2:
return reportValueOrder2(result_name, cell, value1, comment1,
value2, value3, table_unit, digits);
return reportValueOrder2(result_name, cell, value1, comment1, value2, value3,
table_unit, digits);
case 3:
return reportValueOrder3(result_name, cell, value1, comment1,
value2, value3, table_unit, digits);
return reportValueOrder3(result_name, cell, value1, comment1, value2, value3,
table_unit, digits);
default:
return "";
}
@ -1453,12 +1441,12 @@ Table::reportValueOrder3(const char *result_name,
result += " ";
result += unit1->asString(axis1_->axisValue(axis_index1 + 1), digits);
result += " v / ";
result += table_unit->asString(value(axis_index1 + 1, axis_index2,
axis_index3), digits);
result += table_unit->asString(value(axis_index1 + 1, axis_index2, axis_index3),
digits);
if (axis3_->size() != 1) {
result += " ";
result += table_unit->asString(value(axis_index1 + 1, axis_index2,
axis_index3 + 1), digits);
result += table_unit->asString(
value(axis_index1 + 1, axis_index2, axis_index3 + 1), digits);
}
}
else {
@ -1470,22 +1458,22 @@ Table::reportValueOrder3(const char *result_name,
result += " ";
result += unit2->asString(axis2_->axisValue(axis_index2), digits);
result += " | ";
result += table_unit->asString(value(axis_index1, axis_index2,
axis_index3), digits);
result +=
table_unit->asString(value(axis_index1, axis_index2, axis_index3), digits);
if (axis3_->size() != 1) {
result += " ";
result += table_unit->asString(value(axis_index1, axis_index2,
axis_index3 + 1), digits);
result += table_unit->asString(value(axis_index1, axis_index2, axis_index3 + 1),
digits);
}
result += '\n';
result += " |/ ";
if (axis1_->size() != 1 && axis2_->size() != 1) {
result += table_unit->asString(value(axis_index1 + 1, axis_index2 + 1,
axis_index3), digits);
result += table_unit->asString(
value(axis_index1 + 1, axis_index2 + 1, axis_index3), digits);
if (axis3_->size() != 1) {
result += " ";
result += table_unit->asString(value(axis_index1 + 1, axis_index2 + 1,
axis_index3 + 1), digits);
result += table_unit->asString(
value(axis_index1 + 1, axis_index2 + 1, axis_index3 + 1), digits);
}
}
result += '\n';
@ -1493,12 +1481,12 @@ Table::reportValueOrder3(const char *result_name,
result += unit2->asString(axis2_->axisValue(axis_index2 + 1), digits);
result += " | ";
if (axis2_->size() != 1) {
result += table_unit->asString(value(axis_index1, axis_index2 + 1,
axis_index3), digits);
result += table_unit->asString(value(axis_index1, axis_index2 + 1, axis_index3),
digits);
if (axis3_->size() != 1) {
result += " ";
result += table_unit->asString(value(axis_index1, axis_index2 + 1,
axis_index3 + 1), digits);
result += table_unit->asString(
value(axis_index1, axis_index2 + 1, axis_index3 + 1), digits);
}
}
result += '\n';
@ -1516,38 +1504,38 @@ Table::report(const Units *units,
int digits = 4;
const Unit *table_unit = units->timeUnit();
if (order_ == 0) {
report->reportLine("%s", table_unit->asString(value_, digits));
report->report("{}", table_unit->asString(value_, digits));
return;
}
if (order_ == 1) {
const Unit *unit1 = axis1_->unit(units);
report->reportLine("%s", tableVariableString(axis1_->variable()));
report->reportLine("------------------------------");
report->report("{}", tableVariableString(axis1_->variable()));
report->report("------------------------------");
std::string line;
for (size_t index1 = 0; index1 < axis1_->size(); index1++) {
line += unit1->asString(axis1_->axisValue(index1), digits);
line += " ";
}
report->reportLineString(line);
report->reportLine(line);
line.clear();
for (size_t index1 = 0; index1 < axis1_->size(); index1++) {
line += table_unit->asString(value(index1), digits);
line += " ";
}
report->reportLineString(line);
report->reportLine(line);
return;
}
if (order_ == 2) {
const Unit *unit1 = axis1_->unit(units);
const Unit *unit2 = axis2_->unit(units);
report->reportLine("%s", tableVariableString(axis2_->variable()));
report->reportLine(" ------------------------------");
report->report("{}", tableVariableString(axis2_->variable()));
report->report(" ------------------------------");
std::string line = " ";
for (size_t index2 = 0; index2 < axis2_->size(); index2++) {
line += unit2->asString(axis2_->axisValue(index2), digits);
line += " ";
}
report->reportLineString(line);
report->reportLine(line);
for (size_t index1 = 0; index1 < axis1_->size(); index1++) {
line = unit1->asString(axis1_->axisValue(index1), digits);
line += " |";
@ -1555,7 +1543,7 @@ Table::report(const Units *units,
line += table_unit->asString(value(index1, index2), digits);
line += " ";
}
report->reportLineString(line);
report->reportLine(line);
}
return;
}
@ -1564,24 +1552,25 @@ Table::report(const Units *units,
const Unit *unit2 = axis2_->unit(units);
const Unit *unit3 = axis3_->unit(units);
for (size_t axis_index1 = 0; axis_index1 < axis1_->size(); axis_index1++) {
report->reportLine("%s %s", tableVariableString(axis1_->variable()),
unit1->asString(axis1_->axisValue(axis_index1), digits));
report->reportLine("%s", tableVariableString(axis3_->variable()));
report->reportLine(" ------------------------------");
report->report("{} {}", tableVariableString(axis1_->variable()),
unit1->asString(axis1_->axisValue(axis_index1), digits));
report->report("{}", tableVariableString(axis3_->variable()));
report->report(" ------------------------------");
std::string line = " ";
for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) {
line += unit3->asString(axis3_->axisValue(axis_index3), digits);
line += " ";
}
report->reportLineString(line);
report->reportLine(line);
for (size_t axis_index2 = 0; axis_index2 < axis2_->size(); axis_index2++) {
line = unit2->asString(axis2_->axisValue(axis_index2), digits);
line += " |";
for (size_t axis_index3 = 0; axis_index3 < axis3_->size(); axis_index3++) {
line += table_unit->asString(value(axis_index1, axis_index2, axis_index3), digits);
line += table_unit->asString(value(axis_index1, axis_index2, axis_index3),
digits);
line += " ";
}
report->reportLineString(line);
report->reportLine(line);
}
}
}
@ -1626,9 +1615,7 @@ bool
TableAxis::inBounds(float value) const
{
size_t size = values_.size();
return size > 1
&& value >= values_[0]
&& value <= values_[size - 1];
return size > 1 && value >= values_[0] && value <= values_[size - 1];
}
size_t
@ -1670,9 +1657,7 @@ TableAxis::findAxisIndex(float value,
bool &exists) const
{
size_t size = values_.size();
if (size != 0
&& value >= values_[0]
&& value <= values_[size - 1]) {
if (size != 0 && value >= values_[0] && value <= values_[size - 1]) {
int lower = -1;
int upper = size;
while (upper - lower > 1) {
@ -1730,27 +1715,28 @@ TableAxis::unit(const Units *units)
////////////////////////////////////////////////////////////////
static EnumNameMap<TableAxisVariable> table_axis_variable_map =
{{TableAxisVariable::total_output_net_capacitance, "total_output_net_capacitance"},
{TableAxisVariable::equal_or_opposite_output_net_capacitance, "equal_or_opposite_output_net_capacitance"},
{TableAxisVariable::input_net_transition, "input_net_transition"},
{TableAxisVariable::input_transition_time, "input_transition_time"},
{TableAxisVariable::related_pin_transition, "related_pin_transition"},
{TableAxisVariable::constrained_pin_transition, "constrained_pin_transition"},
{TableAxisVariable::output_pin_transition, "output_pin_transition"},
{TableAxisVariable::connect_delay, "connect_delay"},
{TableAxisVariable::related_out_total_output_net_capacitance,
"related_out_total_output_net_capacitance"},
{TableAxisVariable::time, "time"},
{TableAxisVariable::iv_output_voltage, "iv_output_voltage"},
{TableAxisVariable::input_noise_width, "input_noise_width"},
{TableAxisVariable::input_noise_height, "input_noise_height"},
{TableAxisVariable::input_voltage, "input_voltage"},
{TableAxisVariable::output_voltage, "output_voltage"},
{TableAxisVariable::path_depth, "path_depth"},
{TableAxisVariable::path_distance, "path_distance"},
{TableAxisVariable::normalized_voltage, "normalized_voltage"}
};
static EnumNameMap<TableAxisVariable> table_axis_variable_map = {
{TableAxisVariable::total_output_net_capacitance,
"total_output_net_capacitance"},
{TableAxisVariable::equal_or_opposite_output_net_capacitance,
"equal_or_opposite_output_net_capacitance"},
{TableAxisVariable::input_net_transition, "input_net_transition"},
{TableAxisVariable::input_transition_time, "input_transition_time"},
{TableAxisVariable::related_pin_transition, "related_pin_transition"},
{TableAxisVariable::constrained_pin_transition, "constrained_pin_transition"},
{TableAxisVariable::output_pin_transition, "output_pin_transition"},
{TableAxisVariable::connect_delay, "connect_delay"},
{TableAxisVariable::related_out_total_output_net_capacitance,
"related_out_total_output_net_capacitance"},
{TableAxisVariable::time, "time"},
{TableAxisVariable::iv_output_voltage, "iv_output_voltage"},
{TableAxisVariable::input_noise_width, "input_noise_width"},
{TableAxisVariable::input_noise_height, "input_noise_height"},
{TableAxisVariable::input_voltage, "input_voltage"},
{TableAxisVariable::output_voltage, "output_voltage"},
{TableAxisVariable::path_depth, "path_depth"},
{TableAxisVariable::path_distance, "path_distance"},
{TableAxisVariable::normalized_voltage, "normalized_voltage"}};
TableAxisVariable
stringTableAxisVariable(const char *variable)
@ -1769,30 +1755,30 @@ tableVariableUnit(TableAxisVariable variable,
const Units *units)
{
switch (variable) {
case TableAxisVariable::total_output_net_capacitance:
case TableAxisVariable::related_out_total_output_net_capacitance:
case TableAxisVariable::equal_or_opposite_output_net_capacitance:
return units->capacitanceUnit();
case TableAxisVariable::input_net_transition:
case TableAxisVariable::input_transition_time:
case TableAxisVariable::related_pin_transition:
case TableAxisVariable::constrained_pin_transition:
case TableAxisVariable::output_pin_transition:
case TableAxisVariable::connect_delay:
case TableAxisVariable::time:
case TableAxisVariable::input_noise_height:
return units->timeUnit();
case TableAxisVariable::input_voltage:
case TableAxisVariable::output_voltage:
case TableAxisVariable::iv_output_voltage:
case TableAxisVariable::input_noise_width:
return units->voltageUnit();
case TableAxisVariable::path_distance:
return units->distanceUnit();
case TableAxisVariable::path_depth:
case TableAxisVariable::normalized_voltage:
case TableAxisVariable::unknown:
return units->scalarUnit();
case TableAxisVariable::total_output_net_capacitance:
case TableAxisVariable::related_out_total_output_net_capacitance:
case TableAxisVariable::equal_or_opposite_output_net_capacitance:
return units->capacitanceUnit();
case TableAxisVariable::input_net_transition:
case TableAxisVariable::input_transition_time:
case TableAxisVariable::related_pin_transition:
case TableAxisVariable::constrained_pin_transition:
case TableAxisVariable::output_pin_transition:
case TableAxisVariable::connect_delay:
case TableAxisVariable::time:
case TableAxisVariable::input_noise_height:
return units->timeUnit();
case TableAxisVariable::input_voltage:
case TableAxisVariable::output_voltage:
case TableAxisVariable::iv_output_voltage:
case TableAxisVariable::input_noise_width:
return units->voltageUnit();
case TableAxisVariable::path_distance:
return units->distanceUnit();
case TableAxisVariable::path_depth:
case TableAxisVariable::normalized_voltage:
case TableAxisVariable::unknown:
return units->scalarUnit();
}
// Prevent warnings from lame compilers.
return nullptr;
@ -1828,12 +1814,13 @@ OutputWaveforms::checkAxes(const TableTemplate *tbl_template)
const TableAxis *axis2 = tbl_template->axis2();
const TableAxis *axis3 = tbl_template->axis3();
return (axis1 && axis1->variable() == TableAxisVariable::input_net_transition
&& axis2->variable() == TableAxisVariable::time
&& axis3 == nullptr)
|| (axis1 && axis1->variable() == TableAxisVariable::input_net_transition
&& axis2 && axis2->variable() == TableAxisVariable::total_output_net_capacitance
&& axis2->variable() == TableAxisVariable::time && axis3 == nullptr)
|| (axis1 && axis1->variable() == TableAxisVariable::input_net_transition
&& axis2
&& axis2->variable() == TableAxisVariable::total_output_net_capacitance
&& axis3->variable() == TableAxisVariable::time)
|| (axis1 && axis1->variable() == TableAxisVariable::total_output_net_capacitance
|| (axis1
&& axis1->variable() == TableAxisVariable::total_output_net_capacitance
&& axis2 && axis2->variable() == TableAxisVariable::input_net_transition
&& axis3->variable() == TableAxisVariable::time);
}
@ -1886,8 +1873,8 @@ OutputWaveforms::findVoltages(size_t wave_index,
// Make voltage -> current table.
FloatSeq axis_volts = volts;
TableAxisPtr volt_axis =
std::make_shared<TableAxis>(TableAxisVariable::input_voltage, std::move(axis_volts));
TableAxisPtr volt_axis = std::make_shared<TableAxis>(
TableAxisVariable::input_voltage, std::move(axis_volts));
FloatSeq *currents1 = new FloatSeq(*currents->values());
Table *volt_currents = new Table(currents1, volt_axis);
voltage_currents_[wave_index] = volt_currents;
@ -1906,7 +1893,8 @@ OutputWaveforms::currentWaveform(float slew,
times->push_back(time);
currents->push_back(current);
}
TableAxisPtr time_axis = std::make_shared<TableAxis>(TableAxisVariable::time, std::move(*times));
TableAxisPtr time_axis =
std::make_shared<TableAxis>(TableAxisVariable::time, std::move(*times));
delete times;
return Table(currents, time_axis);
}
@ -1988,11 +1976,8 @@ OutputWaveforms::voltageTime1(double volt,
double y01 = voltageTime2(volt, wave_index01);
double y10 = voltageTime2(volt, wave_index10);
double y11 = voltageTime2(volt, wave_index11);
double time
= (1 - dx1) * (1 - dx2) * y00
+ dx1 * (1 - dx2) * y10
+ dx1 * dx2 * y11
+ (1 - dx1) * dx2 * y01;
double time = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10 + dx1 * dx2 * y11
+ (1 - dx1) * dx2 * y01;
return time;
}
@ -2057,11 +2042,8 @@ OutputWaveforms::waveformValue(float slew,
double y01 = waveform01->findValueClip(axis_value);
double y10 = waveform10->findValueClip(axis_value);
double y11 = waveform11->findValueClip(axis_value);
double wave_value
= (1 - dx1) * (1 - dx2) * y00
+ dx1 * (1 - dx2) * y10
+ dx1 * dx2 * y11
+ (1 - dx1) * dx2 * y01;
double wave_value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10
+ dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01;
return wave_value;
}
@ -2083,8 +2065,8 @@ OutputWaveforms::voltageWaveform(float slew,
times.push_back(time);
volts.push_back(volt);
}
TableAxisPtr time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
std::move(times));
TableAxisPtr time_axis =
std::make_shared<TableAxis>(TableAxisVariable::time, std::move(times));
return Table(std::move(volts), time_axis);
}
@ -2101,8 +2083,8 @@ OutputWaveforms::voltageWaveformRaw(float slew,
float
OutputWaveforms::voltageTime(float slew,
float cap,
float volt)
float cap,
float volt)
{
size_t slew_index = slew_axis_->findAxisIndex(slew);
size_t cap_index = cap_axis_->findAxisIndex(cap);
@ -2187,11 +2169,8 @@ OutputWaveforms::beginEndTime(float slew,
y11 = waveform11->axis1()->max();
}
float wave_value
= (1 - dx1) * (1 - dx2) * y00
+ dx1 * (1 - dx2) * y10
+ dx1 * dx2 * y11
+ (1 - dx1) * dx2 * y01;
float wave_value = (1 - dx1) * (1 - dx2) * y00 + dx1 * (1 - dx2) * y10
+ dx1 * dx2 * y11 + (1 - dx1) * dx2 * y01;
return wave_value;
}
@ -2207,8 +2186,8 @@ OutputWaveforms::voltageCurrentWaveform(float slew,
volts->push_back(volt);
currents->push_back(current);
}
TableAxisPtr volt_axis =
std::make_shared<TableAxis>(TableAxisVariable::input_voltage, std::move(*volts));
TableAxisPtr volt_axis = std::make_shared<TableAxis>(
TableAxisVariable::input_voltage, std::move(*volts));
delete volts;
return Table(currents, volt_axis);
}
@ -2250,11 +2229,11 @@ DriverWaveform::waveform(float slew)
time_values->push_back(time);
volt_values->push_back(volt);
}
TableAxisPtr time_axis = std::make_shared<TableAxis>(TableAxisVariable::time,
std::move(*time_values));
TableAxisPtr time_axis =
std::make_shared<TableAxis>(TableAxisVariable::time, std::move(*time_values));
delete time_values;
Table waveform(volt_values, time_axis);
return waveform;
}
} // namespace
} // namespace sta

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "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

View File

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

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "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

View File

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

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "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

View File

@ -25,6 +25,7 @@
#pragma once
#include <map>
#include <string_view>
#include "Zlib.hh"
#include "StringUtil.hh"
@ -65,10 +66,6 @@ public:
const std::string &filename() const { return filename_; }
// Translate from spf/spef namespace to sta namespace.
char *translated(const char *token);
void warn(int id,
const char *fmt,
...)
__attribute__((format (printf, 3, 4)));
void setBusBrackets(char left,
char right);
void setTimeScale(float scale,
@ -108,6 +105,15 @@ public:
char *node_name2,
SpefTriple *res);
PortDirection *portDirection(char *spef_dir);
int warnLine() const;
template <typename... Args>
void warn(int id,
std::string_view fmt,
Args &&...args)
{
report_->fileWarn(id, filename_, warnLine(), fmt,
std::forward<Args>(args)...);
}
private:
Pin *findPinRelative(const char *name);

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "ReportPower.hh"
@ -29,7 +29,7 @@
#include "Report.hh"
#include "Network.hh"
#include "StringUtil.hh"
#include "Format.hh"
namespace sta {
@ -51,32 +51,31 @@ ReportPower::reportDesign(PowerResult &total,
float design_switching = total.switching();
float design_leakage = total.leakage();
float design_total = total.total();
int field_width = std::max(digits + 6, 10);
reportTitle5("Group", "Internal", "Switching", "Leakage", "Total",
field_width);
reportTitle5("Group", "Internal", "Switching", "Leakage", "Total", field_width);
reportTitle5Units(" ", "Power", "Power", "Power", "Power", "(Watts)",
field_width);
reportTitleDashes5(field_width);
reportRow("Sequential", sequential, design_total, field_width, digits);
reportRow("Combinational", combinational, design_total, field_width, digits);
reportRow("Clock", clock, design_total, field_width, digits);
reportRow("Macro", macro, design_total, field_width, digits);
reportRow("Pad", pad, design_total, field_width, digits);
reportTitleDashes5(field_width);
// Report total row using the totals PowerResult
reportRow("Total", total, design_total, field_width, digits);
// Report percentage line
std::string percent_line = stdstrPrint("%-20s", "");
std::string percent_line = sta::format("{:<20}", "");
percent_line += powerColPercent(design_internal, design_total, field_width);
percent_line += powerColPercent(design_switching, design_total, field_width);
percent_line += powerColPercent(design_leakage, design_total, field_width);
report_->reportLineString(percent_line);
report_->reportLine(percent_line);
}
void
@ -84,13 +83,11 @@ ReportPower::reportInsts(const InstPowers &inst_pwrs,
int digits)
{
int field_width = std::max(digits + 6, 10);
reportTitle4("Internal", "Switching", "Leakage", "Total",
field_width);
reportTitle4Units("Power", "Power", "Power", "Power", "(Watts)",
field_width);
reportTitle4("Internal", "Switching", "Leakage", "Total", field_width);
reportTitle4Units("Power", "Power", "Power", "Power", "(Watts)", field_width);
reportTitleDashes4(field_width);
for (const InstPower &inst_pwr : inst_pwrs) {
reportInst(inst_pwr.first, inst_pwr.second, field_width, digits);
}
@ -106,14 +103,14 @@ ReportPower::reportInst(const Instance *inst,
float switching = power.switching();
float leakage = power.leakage();
float total = power.total();
std::string line = powerCol(internal, field_width, digits);
line += powerCol(switching, field_width, digits);
line += powerCol(leakage, field_width, digits);
line += powerCol(total, field_width, digits);
line += " ";
line += network_->pathName(inst);
report_->reportLineString(line);
report_->reportLine(line);
}
std::string
@ -122,9 +119,9 @@ ReportPower::powerCol(float pwr,
int digits)
{
if (std::isnan(pwr))
return stdstrPrint(" %*s", field_width, "NaN");
return sta::format(" {:>{}}", "NaN", field_width);
else
return stdstrPrint(" %*.*e", field_width, digits, pwr);
return sta::format(" {:{}.{}e}", pwr, field_width, digits);
}
std::string
@ -136,41 +133,33 @@ ReportPower::powerColPercent(float col_total,
if (total != 0.0 && !std::isnan(total)) {
percent = col_total / total * 100.0;
}
return stdstrPrint("%*.*f%%", field_width, 1, percent);
return sta::format("{:{}.1f}%", percent, field_width);
}
void
ReportPower::reportTitle5(const char *title1,
const char *title2,
const char *title3,
const char *title4,
const char *title5,
int field_width)
const char *title2,
const char *title3,
const char *title4,
const char *title5,
int field_width)
{
report_->reportLine("%-20s %*s %*s %*s %*s",
title1,
field_width, title2,
field_width, title3,
field_width, title4,
field_width, title5);
report_->report("{:<20} {:>{}} {:>{}} {:>{}} {:>{}}", title1, title2, field_width,
title3, field_width, title4, field_width, title5, field_width);
}
void
ReportPower::reportTitle5Units(const char *title1,
const char *title2,
const char *title3,
const char *title4,
const char *title5,
const char *units,
int field_width)
const char *title2,
const char *title3,
const char *title4,
const char *title5,
const char *units,
int field_width)
{
report_->reportLine("%-20s %*s %*s %*s %*s %s",
title1,
field_width, title2,
field_width, title3,
field_width, title4,
field_width, title5,
units);
report_->report("{:<20} {:>{}} {:>{}} {:>{}} {:>{}} {}", title1, title2,
field_width, title3, field_width, title4, field_width, title5,
field_width, units);
}
void
@ -178,7 +167,7 @@ ReportPower::reportTitleDashes5(int field_width)
{
int count = 20 + (field_width + 1) * 4;
std::string dashes(count, '-');
report_->reportLineString(dashes);
report_->reportLine(dashes);
}
void
@ -192,18 +181,18 @@ ReportPower::reportRow(const char *type,
float switching = power.switching();
float leakage = power.leakage();
float total = power.total();
float percent = 0.0;
if (design_total != 0.0 && !std::isnan(design_total))
percent = total / design_total * 100.0;
std::string line = stdstrPrint("%-20s", type);
std::string line = sta::format("{:<20}", type);
line += powerCol(internal, field_width, digits);
line += powerCol(switching, field_width, digits);
line += powerCol(leakage, field_width, digits);
line += powerCol(total, field_width, digits);
line += stdstrPrint(" %5.1f%%", percent);
report_->reportLineString(line);
line += sta::format(" {:5.1f}%", percent);
report_->reportLine(line);
}
void
@ -213,11 +202,8 @@ ReportPower::reportTitle4(const char *title1,
const char *title4,
int field_width)
{
report_->reportLine(" %*s %*s %*s %*s",
field_width, title1,
field_width, title2,
field_width, title3,
field_width, title4);
report_->report(" {:>{}} {:>{}} {:>{}} {:>{}}", title1, field_width, title2,
field_width, title3, field_width, title4, field_width);
}
void
@ -228,12 +214,8 @@ ReportPower::reportTitle4Units(const char *title1,
const char *units,
int field_width)
{
report_->reportLine(" %*s %*s %*s %*s %s",
field_width, title1,
field_width, title2,
field_width, title3,
field_width, title4,
units);
report_->report(" {:>{}} {:>{}} {:>{}} {:>{}} {}", title1, field_width, title2,
field_width, title3, field_width, title4, field_width, units);
}
void
@ -241,7 +223,7 @@ ReportPower::reportTitleDashes4(int field_width)
{
int count = (field_width + 1) * 4;
std::string dashes(count, '-');
report_->reportLineString(dashes);
report_->reportLine(dashes);
}
} // namespace
} // namespace sta

View File

@ -42,7 +42,7 @@ void
sta::SaifParse::error(const location_type &loc,
const std::string &msg)
{
reader->report()->fileError(169,reader->filename(),loc.begin.line,"%s",msg.c_str());
reader->report()->fileError(169,reader->filename(),loc.begin.line,{}, msg);
}
%}

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "power/SaifReader.hh"
@ -61,7 +61,7 @@ SaifReader::SaifReader(const char *filename,
scope_(scope),
divider_('/'),
escape_('\\'),
timescale_(1.0E-9F), // default units of ns
timescale_(1.0E-9F), // default units of ns
duration_(0.0),
in_scope_level_(0),
power_(sta->power())
@ -78,7 +78,7 @@ SaifReader::read()
SaifParse parser(&scanner, this);
// yyparse returns 0 on success.
bool success = (parser.parse() == 0);
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
report_->report("Annotated {} pin activities.", annotated_pins_.size());
return success;
}
else
@ -95,9 +95,7 @@ void
SaifReader::setTimescale(uint64_t multiplier,
const char *units)
{
if (multiplier == 1
|| multiplier == 10
|| multiplier == 100) {
if (multiplier == 1 || multiplier == 10 || multiplier == 100) {
if (stringEq(units, "us"))
timescale_ = multiplier * 1E-6;
else if (stringEq(units, "ns"))
@ -107,10 +105,10 @@ SaifReader::setTimescale(uint64_t multiplier,
else if (stringEq(units, "fs"))
timescale_ = multiplier * 1E-15;
else
report_->error(180, "SAIF TIMESCALE units not us, ns, or ps.");
report_->error(1861, "SAIF TIMESCALE units not us, ns, or ps.");
}
else
report_->error(181, "SAIF TIMESCALE multiplier not 1, 10, or 100.");
report_->error(1862, "SAIF TIMESCALE multiplier not 1, 10, or 100.");
stringDelete(units);
}
@ -168,8 +166,7 @@ SaifReader::setNetDurations(const char *net_name,
std::string unescaped_name = unescaped(net_name);
const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str());
LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr;
if (pin
&& !sdc_network_->isHierarchical(pin)
if (pin && !sdc_network_->isHierarchical(pin)
&& !sdc_network_->direction(pin)->isInternal()
&& !(liberty_port && liberty_port->isPwrGnd())) {
double t1 = durations[static_cast<int>(SaifState::T1)];
@ -177,13 +174,8 @@ SaifReader::setNetDurations(const char *net_name,
double tc = durations[static_cast<int>(SaifState::TC)];
float density = tc / (duration_ * timescale_);
debugPrint(debug_, "read_saif", 2,
"%s duty %.0f / %" PRIu64 " = %.2f tc %.0f density %.2f",
sdc_network_->pathName(pin),
t1,
duration_,
duty,
tc,
density);
"{} duty {:.0f} / {} = {:.2f} tc {:.0f} density {:.2f}",
sdc_network_->pathName(pin), t1, duration_, duty, tc, density);
power_->setUserActivity(pin, density, duty, PwrActivityOrigin::saif);
annotated_pins_.insert(pin);
}
@ -202,7 +194,7 @@ SaifReader::unescaped(const char *token)
// Just the normal noises.
unescaped += ch;
}
debugPrint(debug_, "saif_name", 1, "token %s -> %s", token, unescaped.c_str());
debugPrint(debug_, "saif_name", 1, "token {} -> {}", token, unescaped);
return unescaped;
}
@ -222,7 +214,7 @@ SaifScanner::SaifScanner(std::istream *stream,
void
SaifScanner::error(const char *msg)
{
report_->fileError(1868, filename_.c_str(), lineno(), "%s", msg);
report_->fileError(1860, filename_.c_str(), lineno(), "{}", msg);
}
} // namespace
} // namespace sta

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "VcdParse.hh"
@ -80,16 +80,14 @@ VcdParse::read(const char *filename,
else if (token[0] == '#') {
try {
time_ = stoll(token.substr(1));
} catch (std::invalid_argument &error) {
report_->fileError(805, filename_, file_line_, "invalid time {}",
token.substr(1));
} catch (std::out_of_range &error) {
report_->fileError(806, filename_, file_line_, "time out of range {}",
token.substr(1));
}
catch (std::invalid_argument &error) {
report_->fileError(805, filename_, file_line_, "invalid time %s",
token.substr(1).c_str());
}
catch (std::out_of_range &error) {
report_->fileError(806, filename_, file_line_, "time out of range %s",
token.substr(1).c_str());
}
reader_->setTimeMin(time_);
reader_->setTimeMin(time_);
prev_time_ = time_;
}
else if (token[0] == '$')
@ -151,37 +149,34 @@ VcdParse::setTimeUnit(const std::string &time_unit,
reader_->setTimeUnit(time_unit, time_unit_scale, time_scale);
}
static EnumNameMap<VcdVarType> vcd_var_type_map =
{{VcdVarType::wire, "wire"},
{VcdVarType::reg, "reg"},
{VcdVarType::parameter, "parameter"},
{VcdVarType::integer, "integer"},
{VcdVarType::real, "real"},
{VcdVarType::supply0, "supply0"},
{VcdVarType::supply1, "supply1"},
{VcdVarType::time, "time"},
{VcdVarType::tri, "tri"},
{VcdVarType::triand, "triand"},
{VcdVarType::trior, "trior"},
{VcdVarType::trireg, "trireg"},
{VcdVarType::tri0, "tri0"},
{VcdVarType::tri1, "tri1"},
{VcdVarType::wand, "wand"},
{VcdVarType::wor, "wor"}
};
static EnumNameMap<VcdVarType> vcd_var_type_map = {
{VcdVarType::wire, "wire"},
{VcdVarType::reg, "reg"},
{VcdVarType::parameter, "parameter"},
{VcdVarType::integer, "integer"},
{VcdVarType::real, "real"},
{VcdVarType::supply0, "supply0"},
{VcdVarType::supply1, "supply1"},
{VcdVarType::time, "time"},
{VcdVarType::tri, "tri"},
{VcdVarType::triand, "triand"},
{VcdVarType::trior, "trior"},
{VcdVarType::trireg, "trireg"},
{VcdVarType::tri0, "tri0"},
{VcdVarType::tri1, "tri1"},
{VcdVarType::wand, "wand"},
{VcdVarType::wor, "wor"}};
void
VcdParse::parseVar()
{
std::vector<std::string> tokens = readStmtTokens();
if (tokens.size() == 4
|| tokens.size() == 5) {
if (tokens.size() == 4 || tokens.size() == 5) {
std::string type_name = tokens[0];
VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown);
if (type == VcdVarType::unknown)
report_->fileWarn(1370, filename_, file_line_,
"Unknown variable type %s.",
type_name.c_str());
report_->fileWarn(809, filename_, file_line_, "Unknown variable type {}.",
type_name);
else {
size_t width = std::stoi(tokens[1]);
std::string &id = tokens[2];
@ -229,23 +224,18 @@ VcdParse::parseVarValues()
if (time_ > prev_time_)
reader_->varMinDeltaTime(time_ - prev_time_);
}
else if (char0 == '0'
|| char0 == '1'
|| char0 == 'X'
|| char0 == 'U'
else if (char0 == '0' || char0 == '1' || char0 == 'X' || char0 == 'U'
|| char0 == 'Z') {
std::string id = token.substr(1);
if (!reader_->varIdValid(id))
report_->fileError(805, filename_, file_line_,
"unknown variable %s", id.c_str());
report_->fileError(808, filename_, file_line_, "unknown variable {}", id);
reader_->varAppendValue(id, time_, char0);
}
else if (char0 == 'B') {
std::string bus_value = token.substr(1);
std::string id = getToken();
if (!reader_->varIdValid(id))
report_->fileError(807, filename_, file_line_,
"unknown variable %s", id.c_str());
report_->fileError(807, filename_, file_line_, "unknown variable {}", id);
else {
// Reverse the bus value to match the bit order in the VCD file.
std::reverse(bus_value.begin(), bus_value.end());
@ -327,4 +317,4 @@ VcdValue::setValue(VcdTime time,
value_ = value;
}
} // namespace
} // namespace sta

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "VcdReader.hh"
@ -89,12 +89,10 @@ VcdCount::incrCounts(VcdTime time,
if (prev_value_ == '1')
high_time_ += time - prev_time_;
if (value != prev_value_)
transition_count_ += (value == 'X'
|| value == 'Z'
|| prev_value_ == 'X'
|| prev_value_ == 'Z')
? .5
: 1.0;
transition_count_ +=
(value == 'X' || value == 'Z' || prev_value_ == 'X' || prev_value_ == 'Z')
? .5
: 1.0;
}
prev_time_ = time;
prev_value_ = value;
@ -216,8 +214,7 @@ VcdCountReader::makeVar(const VcdScope &scope,
size_t width,
const std::string &id)
{
if (type == VcdVarType::wire
|| type == VcdVarType::reg) {
if (type == VcdVarType::wire || type == VcdVarType::reg) {
std::string path_name;
bool first = true;
for (const std::string &context : scope) {
@ -228,24 +225,23 @@ VcdCountReader::makeVar(const VcdScope &scope,
}
size_t scope_length = scope_.size();
// string::starts_with in c++20
if (scope_length == 0
|| path_name.substr(0, scope_length) == scope_) {
if (scope_length == 0 || path_name.substr(0, scope_length) == scope_) {
path_name += '/';
path_name += name;
// Strip the scope from the name.
std::string var_scoped = path_name.substr(scope_length + 1);
if (width == 1) {
std::string pin_name = netVerilogToSta(&var_scoped);
std::string pin_name = netVerilogToSta(var_scoped);
addVarPin(pin_name, id, width, 0);
}
else {
bool is_bus, is_range, subscript_wild;
std::string bus_name;
int from, to;
parseBusName(var_scoped.c_str(), '[', ']', '\\',
is_bus, is_range, bus_name, from, to, subscript_wild);
parseBusName(var_scoped.c_str(), '[', ']', '\\', is_bus, is_range, bus_name,
from, to, subscript_wild);
if (is_bus) {
std::string sta_bus_name = netVerilogToSta(&bus_name);
std::string sta_bus_name = netVerilogToSta(bus_name);
int bit_idx = 0;
if (to < from) {
for (int bus_bit = to; bus_bit <= from; bus_bit++) {
@ -269,7 +265,7 @@ VcdCountReader::makeVar(const VcdScope &scope,
}
}
else
report_->warn(1451, "problem parsing bus %s.", var_scoped.c_str());
report_->warn(1451, "problem parsing bus {}.", var_scoped);
}
}
}
@ -283,17 +279,14 @@ VcdCountReader::addVarPin(const std::string &pin_name,
{
const Pin *pin = sdc_network_->findPin(pin_name.c_str());
LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr;
if (pin
&& !sdc_network_->isHierarchical(pin)
if (pin && !sdc_network_->isHierarchical(pin)
&& !sdc_network_->direction(pin)->isInternal()
&& !sdc_network_->direction(pin)->isPowerGround()
&& !(liberty_port && liberty_port->isPwrGnd())) {
VcdCounts &vcd_counts = vcd_count_map_[id];
vcd_counts.resize(width);
vcd_counts[bit_idx].addPin(pin);
debugPrint(debug_, "read_vcd", 2, "id %s pin %s",
id.c_str(),
pin_name.c_str());
debugPrint(debug_, "read_vcd", 2, "id {} pin {}", id, pin_name);
}
}
@ -309,10 +302,8 @@ VcdCountReader::varAppendValue(const std::string &id,
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
VcdCount &vcd_count = vcd_counts[bit_idx];
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c",
sdc_network_->pathName(pin),
time,
value);
debugPrint(debug_, "read_vcd", 3, "{} time {} value {}",
sdc_network_->pathName(pin), time, value);
}
}
}
@ -335,7 +326,7 @@ VcdCountReader::varAppendBusValue(const std::string &id,
char bit_value;
if (bus_value.size() == 1)
bit_value = bus_value[0];
else if (bit_idx < bus_value.size())
else if (bit_idx < bus_value.size())
bit_value = bus_value[bit_idx];
else
bit_value = '0';
@ -343,10 +334,8 @@ VcdCountReader::varAppendBusValue(const std::string &id,
vcd_count.incrCounts(time, bit_value);
if (debug_->check("read_vcd", 3)) {
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c",
sdc_network_->pathName(pin),
time,
bit_value);
debugPrint(debug_, "read_vcd", 3, "{} time {} value {}",
sdc_network_->pathName(pin), time, bit_value);
}
}
}
@ -371,7 +360,7 @@ private:
const std::string filename_;
std::set<const Pin*> annotated_pins_;
std::set<const Pin *> annotated_pins_;
VcdCountReader vcd_reader_;
VcdParse vcd_parse_;
const Sdc *sdc_;
@ -398,8 +387,12 @@ ReadVcdActivities::ReadVcdActivities(const std::string &filename,
Sta *sta) :
StaState(sta),
filename_(filename),
vcd_reader_(scope, sdc_network_, report_, debug_),
vcd_parse_(report_, debug_),
vcd_reader_(scope,
sdc_network_,
report_,
debug_),
vcd_parse_(report_,
debug_),
sdc_(sdc),
power_(sta->power())
{
@ -418,7 +411,7 @@ ReadVcdActivities::readActivities()
setActivities();
else
report_->warn(1450, "VCD max time is zero.");
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
report_->report("Annotated {} pin activities.", annotated_pins_.size());
}
void
@ -428,7 +421,7 @@ ReadVcdActivities::setActivities()
VcdTime time_max = vcd_reader_.timeMax();
VcdTime time_delta = time_max - time_min;
double time_scale = vcd_reader_.timeScale();
for (auto& [id, vcd_counts] : vcd_reader_.countMap()) {
for (auto &[id, vcd_counts] : vcd_reader_.countMap()) {
for (const VcdCount &vcd_count : vcd_counts) {
double transition_count = vcd_count.transitionCount();
VcdTime high_time = vcd_count.highTime(time_max);
@ -437,11 +430,8 @@ ReadVcdActivities::setActivities()
if (debug_->check("read_vcd", 1)) {
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd", 1,
"%s transitions %.1f activity %.2f duty %.2f",
sdc_network_->pathName(pin),
transition_count,
density,
duty);
"{} transitions {:.1f} activity {:.2f} duty {:.2f}",
sdc_network_->pathName(pin), transition_count, density, duty);
}
}
for (const Pin *pin : vcd_count.pins()) {
@ -463,23 +453,24 @@ ReadVcdActivities::checkClkPeriod(const Pin *pin,
VcdTime time_max = vcd_reader_.timeMax();
VcdTime time_min = vcd_reader_.timeMin();
double time_scale = vcd_reader_.timeScale();
double sim_period = (time_max - time_min) * time_scale / (transition_count / 2.0);
double sim_period =
(time_max - time_min) * time_scale / (transition_count / 2.0);
for (Clock *clk : *clks) {
if (transition_count == 0)
report_->warn(1453, "clock %s pin %s has no vcd transitions.",
clk->name(),
report_->warn(1453, "clock {} pin {} has no vcd transitions.", clk->name(),
sdc_network_->pathName(pin));
else {
double clk_period = clk->period();
if (std::abs((clk_period - sim_period) / clk_period) > sim_clk_period_tolerance_)
if (std::abs((clk_period - sim_period) / clk_period)
> sim_clk_period_tolerance_)
// Warn if sim clock period differs from SDC by more than 10%.
report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s",
clk->name(),
delayAsString(sim_period, this),
report_->warn(1452,
"clock {} vcd period {} differs from SDC clock period {}",
clk->name(), delayAsString(sim_period, this),
delayAsString(clk_period, this));
}
}
}
}
} // namespace
} // namespace sta

View File

@ -28,6 +28,7 @@
#include "ContainerHelpers.hh"
#include "Error.hh"
#include "Format.hh"
#include "StringUtil.hh"
#include "MinMax.hh"
#include "Transition.hh"
@ -531,7 +532,7 @@ ClockEdge::ClockEdge(Clock *clock,
const RiseFall *rf) :
clock_(clock),
rf_(rf),
name_(stringPrint("%s %s", clock_->name(), rf_->shortName())),
name_(sta::format("{} {}", clock_->name(), rf_->shortName())),
time_(0.0),
index_(clock_->index() * RiseFall::index_count + rf_->index())
{
@ -539,7 +540,6 @@ ClockEdge::ClockEdge(Clock *clock,
ClockEdge::~ClockEdge()
{
stringDelete(name_);
}
void

View File

@ -29,14 +29,14 @@
namespace sta {
ClockGroups::ClockGroups(const char *name,
ClockGroups::ClockGroups(const std::string &name,
bool logically_exclusive,
bool physically_exclusive,
bool asynchronous,
bool allow_paths,
const char *comment) :
SdcCmdComment(comment),
name_(stringCopy(name)),
name_(name),
logically_exclusive_(logically_exclusive),
physically_exclusive_(physically_exclusive),
asynchronous_(asynchronous),
@ -46,7 +46,6 @@ ClockGroups::ClockGroups(const char *name,
ClockGroups::~ClockGroups()
{
stringDelete(name_);
deleteContents(groups_);
}

View File

@ -93,7 +93,7 @@ CycleAcctings::reportClkToClkMaxCycleWarnings(Report *report)
ClockPair clk_pair2(tgt_clk, src_clk);
if (!clk_warnings.contains(clk_pair1)
&& !clk_warnings.contains(clk_pair2)) {
report->warn(1010, "No common period was found between clocks %s and %s.",
report->warn(1010, "No common period was found between clocks {} and {}.",
src_clk->name(),
tgt_clk->name());
clk_warnings.insert(clk_pair1);
@ -126,7 +126,7 @@ CycleAccting::findDelays(StaState *sta)
{
Debug *debug = sta->debug();
const Unit *time_unit = sta->units()->timeUnit();
debugPrint(debug, "cycle_acct", 1, "%s -> %s",
debugPrint(debug, "cycle_acct", 1, "{} -> {}",
src_->name(),
tgt_->name());
const int setup_index = TimingRole::setup()->index();
@ -167,14 +167,14 @@ CycleAccting::findDelays(StaState *sta)
if (tgt_past_src && src_past_tgt
// Synchronicity achieved.
&& fuzzyEqual(src_cycle_start, tgt_cycle_start)) {
debugPrint(debug, "cycle_acct", 1, " setup = %s, required = %s",
debugPrint(debug, "cycle_acct", 1, " setup = {}, required = {}",
time_unit->asString(delay_[setup_index]),
time_unit->asString(required_[setup_index]));
debugPrint(debug, "cycle_acct", 1, " hold = %s, required = %s",
debugPrint(debug, "cycle_acct", 1, " hold = {}, required = {}",
time_unit->asString(delay_[hold_index]),
time_unit->asString(required_[hold_index]));
debugPrint(debug, "cycle_acct", 1,
" converged at src cycles = %d tgt cycles = %d",
" converged at src cycles = {} tgt cycles = {}",
src_cycle, tgt_cycle);
return;
}
@ -182,13 +182,13 @@ CycleAccting::findDelays(StaState *sta)
if (fuzzyGreater(src_cycle_start, tgt_cycle_start + tgt_period)
&& src_past_tgt)
break;
debugPrint(debug, "cycle_acct", 2, " %s src cycle %d %s + %s = %s",
debugPrint(debug, "cycle_acct", 2, " {} src cycle {} {} + {} = {}",
src_->name(),
src_cycle,
time_unit->asString(src_cycle_start),
time_unit->asString(src_->time()),
time_unit->asString(src_time));
debugPrint(debug, "cycle_acct", 2, " %s tgt cycle %d %s + %s = %s",
debugPrint(debug, "cycle_acct", 2, " {} tgt cycle {} {} + {} = {}",
tgt_->name(),
tgt_cycle,
time_unit->asString(tgt_cycle_start),
@ -203,7 +203,7 @@ CycleAccting::findDelays(StaState *sta)
double required = tgt_time - src_cycle_start;
setSetupAccting(src_cycle, tgt_cycle, delay, required);
debugPrint(debug, "cycle_acct", 2,
" setup min delay = %s, required = %s",
" setup min delay = {}, required = {}",
time_unit->asString(delay_[setup_index]),
time_unit->asString(required_[setup_index]));
}
@ -239,7 +239,7 @@ CycleAccting::findDelays(StaState *sta)
setAccting(TimingRole::latchSetup(),
src_cycle, latch_tgt_cycle, delay, required);
debugPrint(debug, "cycle_acct", 2,
" latch setup min delay = %s, required = %s",
" latch setup min delay = {}, required = {}",
time_unit->asString(delay_[latch_setup_index]),
time_unit->asString(required_[latch_setup_index]));
}
@ -253,7 +253,7 @@ CycleAccting::findDelays(StaState *sta)
double required = tgt_time - src_cycle_start;
setHoldAccting(src_cycle, tgt_cycle, delay, required);
debugPrint(debug, "cycle_acct", 2,
" hold min delay = %s, required = %s",
" hold min delay = {}, required = {}",
time_unit->asString(delay_[hold_index]),
time_unit->asString(required_[hold_index]));
}
@ -268,7 +268,7 @@ CycleAccting::findDelays(StaState *sta)
setAccting(TimingRole::gatedClockHold(),
src_cycle, tgt_cycle, delay, required);
debugPrint(debug, "cycle_acct", 2,
" gated clk hold min delay = %s, required = %s",
" gated clk hold min delay = {}, required = {}",
time_unit->asString(delay_[gclk_hold_index]),
time_unit->asString(required_[gclk_hold_index]));
}
@ -278,7 +278,7 @@ CycleAccting::findDelays(StaState *sta)
}
max_cycles_exceeded_ = true;
debugPrint(debug, "cycle_acct", 1,
" max cycles exceeded after %d src cycles, %d tgt_cycles",
" max cycles exceeded after {} src cycles, {} tgt_cycles",
src_cycle, tgt_cycle);
}
else if (tgt_period > 0.0)

View File

@ -26,6 +26,7 @@
#include <algorithm>
#include "Format.hh"
#include "ContainerHelpers.hh"
#include "MinMax.hh"
#include "TimingRole.hh"
@ -121,17 +122,10 @@ ExceptionPath::~ExceptionPath()
}
}
const char *
ExceptionPath::asString(const Network *network) const
std::string
ExceptionPath::to_string(const Network *network) const
{
const char *from_thru_to = fromThruToString(network);
const char *type = typeString();
size_t length = strlen(type) + strlen(from_thru_to) + 1;
char *result = makeTmpString(length);
char *r = result;
stringAppend(r, type);
stringAppend(r, from_thru_to);
return result;
return sta::format("{}{}", typeString(), fromThruToString(network));
}
void
@ -321,7 +315,7 @@ ExceptionPath::intersectsPts(ExceptionPath *exception,
return false;
}
const char *
std::string
ExceptionPath::fromThruToString(const Network *network) const
{
std::string str;
@ -331,7 +325,7 @@ ExceptionPath::fromThruToString(const Network *network) const
}
if (from_)
str += from_->asString(network);
str += from_->to_string(network);
if (thrus_) {
str += " -thru";
@ -341,7 +335,7 @@ ExceptionPath::fromThruToString(const Network *network) const
if (!first_thru)
str += " &&";
str += " {";
str += thru->asString(network);
str += thru->to_string(network);
str += "}";
first_thru = false;
}
@ -349,11 +343,9 @@ ExceptionPath::fromThruToString(const Network *network) const
}
if (to_)
str += to_->asString(network);
str += to_->to_string(network);
char *result = makeTmpString(str.size() + 1);
strcpy(result, str.c_str());
return result;
return str;
}
ExceptionState *
@ -524,14 +516,12 @@ PathDelay::tighterThan(ExceptionPath *exception) const
return delay_ < exception->delay();
}
const char *
PathDelay::asString(const Network *network) const
std::string
PathDelay::to_string(const Network *network) const
{
const char *from_thru_to = fromThruToString(network);
const char *result = stringPrintTmp("PathDelay %.3fns%s",
delay_ * 1E+9F,
from_thru_to);
return result;
return sta::format("PathDelay {:.3f}ns{}",
delay_ * 1E+9F,
fromThruToString(network));
}
const char *
@ -728,15 +718,13 @@ MultiCyclePath::matches(const MinMax *min_max,
|| (!exactly && min_max == MinMax::min());
}
const char *
MultiCyclePath::asString(const Network *network) const
std::string
MultiCyclePath::to_string(const Network *network) const
{
const char *from_thru_to = fromThruToString(network);
const char *result = stringPrintTmp("Multicycle %s %d%s",
(use_end_clk_) ? "-end" : "-start",
path_multiplier_,
from_thru_to);
return result;
return sta::format("Multicycle {} {}{}",
(use_end_clk_) ? "-end" : "-start",
path_multiplier_,
fromThruToString(network));
}
const char *
@ -826,7 +814,7 @@ FilterPath::resetMatch(ExceptionFrom *,
////////////////////////////////////////////////////////////////
GroupPath::GroupPath(const char *name,
GroupPath::GroupPath(const std::string &name,
bool is_default,
ExceptionFrom *from,
ExceptionThruSeq *thrus,
@ -836,14 +824,13 @@ GroupPath::GroupPath(const char *name,
ExceptionPath(from, thrus, to, MinMaxAll::all(), own_pts,
groupPathPriority() + fromThruToPriority(from, thrus, to),
comment),
name_(stringCopy(name)),
name_(name),
is_default_(is_default)
{
}
GroupPath::~GroupPath()
{
stringDelete(name_);
}
const char *
@ -877,7 +864,7 @@ GroupPath::tighterThan(ExceptionPath *) const
bool
GroupPath::mergeable(ExceptionPath *exception) const
{
return stringEqIf(name_, exception->name())
return name_ == exception->name()
&& ExceptionPath::mergeable(exception)
&& overrides(exception);
}
@ -887,12 +874,12 @@ GroupPath::overrides(ExceptionPath *exception) const
{
return exception->isGroupPath()
&& is_default_ == exception->isDefault()
&& stringEqIf(name_, exception->name());
&& name_ == exception->name();
}
////////////////////////////////////////////////////////////////
const int ExceptionPt::as_string_max_objects_ = 20;
const int ExceptionPt::to_string_max_objects_ = 20;
ExceptionPt::ExceptionPt(const RiseFallBoth *rf,
bool own_pts) :
@ -1181,8 +1168,8 @@ ExceptionFromTo::deletePinBefore(const Pin *pin,
deletePin(pin, network);
}
const char *
ExceptionFromTo::asString(const Network *network) const
std::string
ExceptionFromTo::to_string(const Network *network) const
{
std::string str;
str += " ";
@ -1199,7 +1186,7 @@ ExceptionFromTo::asString(const Network *network) const
str += network->pathName(pin);
first = false;
obj_count++;
if (obj_count > as_string_max_objects_)
if (obj_count > to_string_max_objects_)
break;
}
}
@ -1211,7 +1198,7 @@ ExceptionFromTo::asString(const Network *network) const
str += clk->name();
first = false;
obj_count++;
if (obj_count > as_string_max_objects_)
if (obj_count > to_string_max_objects_)
break;
}
}
@ -1223,18 +1210,16 @@ ExceptionFromTo::asString(const Network *network) const
str += network->pathName(inst);
first = false;
obj_count++;
if (obj_count > as_string_max_objects_)
if (obj_count > to_string_max_objects_)
break;
}
}
if (obj_count == as_string_max_objects_)
if (obj_count == to_string_max_objects_)
str += ", ...";
str += "}";
char *result = makeTmpString(str.size() + 1);
strcpy(result, str.c_str());
return result;
return str;
}
size_t
@ -1355,19 +1340,17 @@ ExceptionTo::clone(const Network *network)
return new ExceptionTo(pins, clks, insts, rf_, end_rf_, true, network);
}
const char *
ExceptionTo::asString(const Network *network) const
std::string
ExceptionTo::to_string(const Network *network) const
{
std::string str;
if (hasObjects())
str += ExceptionFromTo::asString(network);
str += ExceptionFromTo::to_string(network);
if (end_rf_ != RiseFallBoth::riseFall())
str += (end_rf_ == RiseFallBoth::rise()) ? " -rise" : " -fall";
char *result = makeTmpString(str.size() + 1);
strcpy(result, str.c_str());
return result;
return str;
}
bool
@ -1674,8 +1657,8 @@ ExceptionThru::~ExceptionThru()
}
}
const char *
ExceptionThru::asString(const Network *network) const
std::string
ExceptionThru::to_string(const Network *network) const
{
std::string str;
bool first = true;
@ -1688,7 +1671,7 @@ ExceptionThru::asString(const Network *network) const
str += network->pathName(pin);
first = false;
obj_count++;
if (obj_count > as_string_max_objects_)
if (obj_count > to_string_max_objects_)
break;
}
}
@ -1700,7 +1683,7 @@ ExceptionThru::asString(const Network *network) const
str += network->pathName(net);
first = false;
obj_count++;
if (obj_count > as_string_max_objects_)
if (obj_count > to_string_max_objects_)
break;
}
}
@ -1712,20 +1695,18 @@ ExceptionThru::asString(const Network *network) const
str += network->pathName(inst);
first = false;
obj_count++;
if (obj_count > as_string_max_objects_)
if (obj_count > to_string_max_objects_)
break;
}
}
if (obj_count == as_string_max_objects_)
if (obj_count == to_string_max_objects_)
str += ", ...";
if (rf_ == RiseFallBoth::rise())
str += " rise";
else if (rf_ == RiseFallBoth::fall())
str += " fall";
char *result = makeTmpString(str.size() + 1);
strcpy(result, str.c_str());
return result;
return str;
}
ExceptionThruSeq *

View File

@ -1941,40 +1941,38 @@ ClockInsertionkLess::operator()(const ClockInsertion *insert1,
////////////////////////////////////////////////////////////////
ClockGroups *
Sdc::makeClockGroups(const char *name,
Sdc::makeClockGroups(const std::string &name,
bool logically_exclusive,
bool physically_exclusive,
bool asynchronous,
bool allow_paths,
const char *comment)
{
char *gen_name = nullptr;
if (name == nullptr
|| name[0] == '\0')
name = gen_name = makeClockGroupsName();
std::string group_name;
if (name.empty())
group_name = makeClockGroupsName();
else {
ClockGroups *groups = findKey(clk_groups_name_map_, name);
group_name = name;
ClockGroups *groups = findKey(clk_groups_name_map_, group_name);
if (groups)
removeClockGroups(groups);
}
ClockGroups *groups = new ClockGroups(name, logically_exclusive,
ClockGroups *groups = new ClockGroups(group_name, logically_exclusive,
physically_exclusive,
asynchronous, allow_paths, comment);
clk_groups_name_map_[groups->name()] = groups;
stringDelete(gen_name);
return groups;
}
// Generate a name for the clock group.
char *
std::string
Sdc::makeClockGroupsName()
{
char *name = nullptr;
std::string name;
int i = 0;
do {
i++;
stringDelete(name);
name = stringPrint("group%d", i);
name = sta::format("group{}", i);
} while (clk_groups_name_map_.contains(name));
return name;
}
@ -1990,7 +1988,7 @@ void
Sdc::ensureClkGroupExclusions()
{
if (clk_group_exclusions_.empty()) {
for (const auto [name, clk_groups] : clk_groups_name_map_)
for (const auto &[name, clk_groups] : clk_groups_name_map_)
makeClkGroupExclusions(clk_groups);
}
}
@ -2087,7 +2085,7 @@ Sdc::sameClockGroupExplicit(const Clock *clk1,
}
void
Sdc::removeClockGroups(const char *name)
Sdc::removeClockGroups(const std::string &name)
{
ClockGroups *clk_groups = findKey(clk_groups_name_map_, name);
if (clk_groups)
@ -2095,51 +2093,55 @@ Sdc::removeClockGroups(const char *name)
}
void
Sdc::removeClockGroupsLogicallyExclusive(const char *name)
Sdc::removeClockGroupsLogicallyExclusive()
{
if (name) {
ClockGroups *groups = findKey(clk_groups_name_map_, name);
if (groups && groups->logicallyExclusive())
for (const auto &[name, groups] : clk_groups_name_map_) {
if (groups->logicallyExclusive())
removeClockGroups(groups);
}
else {
for (const auto [name, groups] : clk_groups_name_map_) {
if (groups->logicallyExclusive())
removeClockGroups(groups);
}
}
}
void
Sdc::removeClockGroupsPhysicallyExclusive(const char *name)
Sdc::removeClockGroupsLogicallyExclusive(const std::string &name)
{
if (name) {
ClockGroups *groups = findKey(clk_groups_name_map_, name);
if (groups && groups->physicallyExclusive())
ClockGroups *groups = findKey(clk_groups_name_map_, name);
if (groups && groups->logicallyExclusive())
removeClockGroups(groups);
}
void
Sdc::removeClockGroupsPhysicallyExclusive()
{
for (const auto &[name, groups] : clk_groups_name_map_) {
if (groups->physicallyExclusive())
removeClockGroups(groups);
}
else {
for (const auto [name, groups] : clk_groups_name_map_) {
if (groups->physicallyExclusive())
removeClockGroups(groups);
}
}
}
void
Sdc::removeClockGroupsAsynchronous(const char *name)
Sdc::removeClockGroupsPhysicallyExclusive(const std::string &name)
{
if (name) {
ClockGroups *groups = findKey(clk_groups_name_map_, name);
if (groups && groups->asynchronous())
ClockGroups *groups = findKey(clk_groups_name_map_, name);
if (groups && groups->physicallyExclusive())
removeClockGroups(groups);
}
void
Sdc::removeClockGroupsAsynchronous()
{
for (const auto &[name, groups] : clk_groups_name_map_) {
if (groups->asynchronous())
removeClockGroups(groups);
}
else {
for (const auto [name, groups] : clk_groups_name_map_) {
if (groups->asynchronous())
removeClockGroups(groups);
}
}
}
void
Sdc::removeClockGroupsAsynchronous(const std::string &name)
{
ClockGroups *groups = findKey(clk_groups_name_map_, name);
if (groups && groups->asynchronous())
removeClockGroups(groups);
}
void
@ -2155,7 +2157,7 @@ Sdc::removeClockGroups(ClockGroups *groups)
void
Sdc::clockGroupsDeleteClkRefs(Clock *clk)
{
for (const auto [name, groups] : clk_groups_name_map_)
for (const auto &[name, groups] : clk_groups_name_map_)
groups->removeClock(clk);
clearClkGroupExclusions();
}
@ -4080,9 +4082,7 @@ void
Sdc::clearGroupPathMap()
{
// GroupPath exceptions are deleted with other exceptions.
// Delete group_path name strings.
for (auto [name, groups] : group_path_map_) {
stringDelete(name);
deleteContents(*groups);
delete groups;
}
@ -4090,7 +4090,7 @@ Sdc::clearGroupPathMap()
}
void
Sdc::makeGroupPath(const char *name,
Sdc::makeGroupPath(const std::string &name,
bool is_default,
ExceptionFrom *from,
ExceptionThruSeq *thrus,
@ -4098,9 +4098,9 @@ Sdc::makeGroupPath(const char *name,
const char *comment)
{
checkFromThrusTo(from, thrus, to);
if (name && is_default)
if (!name.empty() && is_default)
report_->critical(1490, "group path name and is_default are mutually exclusive.");
else if (name) {
else if (!name.empty()) {
GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to,
true, comment);
// Clone the group_path because it may get merged and hence deleted
@ -4115,7 +4115,7 @@ Sdc::makeGroupPath(const char *name,
GroupPathSet *groups = findKey(group_path_map_, name);
if (groups == nullptr) {
groups = new GroupPathSet(network_);
group_path_map_[stringCopy(name)] = groups;
group_path_map_[name] = groups;
}
if (groups->contains(group_path))
// Exact copy of existing group path.
@ -4226,7 +4226,7 @@ void
Sdc::makeLoopExceptionThru(const Pin *pin,
ExceptionThruSeq *thrus)
{
debugPrint(debug_, "levelize", 2, " %s", network_->pathName(pin));
debugPrint(debug_, "levelize", 2, " {}", network_->pathName(pin));
PinSet *pins = new PinSet(network_);
pins->insert(pin);
ExceptionThru *thru = makeExceptionThru(pins, nullptr, nullptr,
@ -4254,8 +4254,8 @@ Sdc::deleteLoopExceptions()
void
Sdc::addException(ExceptionPath *exception)
{
debugPrint(debug_, "exception_merge", 1, "add exception for %s",
exception->asString(network_));
debugPrint(debug_, "exception_merge", 1, "add exception for {}",
exception->to_string(network_));
if (exception->isPathDelay()) {
recordPathDelayInternalFrom(exception);
@ -4282,8 +4282,8 @@ Sdc::addException(ExceptionPath *exception)
ExceptionTo *to = exception->to();
ExceptionTo *to1 = to ? to->clone(network_) : nullptr;
ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true);
debugPrint(debug_, "exception_merge", 1, " split exception for %s",
exception1->asString(network_));
debugPrint(debug_, "exception_merge", 1, " split exception for {}",
exception1->to_string(network_));
addException1(exception1);
ClockSet *clks2 = new ClockSet(*from->clks());
@ -4292,8 +4292,8 @@ Sdc::addException(ExceptionPath *exception)
ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_);
ExceptionTo *to2 = to ? to->clone(network_) : nullptr;
ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true);
debugPrint(debug_, "exception_merge", 1, " split exception for %s",
exception2->asString(network_));
debugPrint(debug_, "exception_merge", 1, " split exception for {}",
exception2->to_string(network_));
addException1(exception2);
delete exception;
@ -4316,8 +4316,8 @@ Sdc::addException1(ExceptionPath *exception)
ExceptionTo *to1 = new ExceptionTo(pins1, nullptr, insts1, to->transition(),
to->endTransition(), true, network_);
ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true);
debugPrint(debug_, "exception_merge", 1, " split exception for %s",
exception1->asString(network_));
debugPrint(debug_, "exception_merge", 1, " split exception for {}",
exception1->to_string(network_));
addException2(exception1);
ExceptionFrom *from2 = exception->from() ? exception->from()->clone(network_):nullptr;
@ -4326,8 +4326,8 @@ Sdc::addException1(ExceptionPath *exception)
ExceptionTo *to2 = new ExceptionTo(nullptr, clks2, nullptr, to->transition(),
to->endTransition(), true, network_);
ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true);
debugPrint(debug_, "exception_merge", 1, " split exception for %s",
exception2->asString(network_));
debugPrint(debug_, "exception_merge", 1, " split exception for {}",
exception2->to_string(network_));
addException2(exception2);
delete exception;
@ -4389,8 +4389,8 @@ Sdc::addException2(ExceptionPath *exception)
void
Sdc::deleteMatchingExceptions(ExceptionPath *exception)
{
debugPrint(debug_, "exception_merge", 1, "find matches for %s",
exception->asString(network_));
debugPrint(debug_, "exception_merge", 1, "find matches for {}",
exception->to_string(network_));
ExceptionPathSet matches;
findMatchingExceptions(exception, matches);
@ -4676,10 +4676,10 @@ Sdc::recordMergeHash(ExceptionPath *exception,
{
size_t hash = exception->hash(missing_pt);
debugPrint(debug_, "exception_merge", 3,
"record merge hash %zu %s missing %s",
"record merge hash {} {} missing {}",
hash,
exception->asString(network_),
missing_pt->asString(network_));
exception->to_string(network_),
missing_pt->to_string(network_));
ExceptionPathSet &set = exception_merge_hash_[hash];
set.insert(exception);
}
@ -4860,10 +4860,10 @@ Sdc::findMergeMatch(ExceptionPath *exception)
// search at the endpoint.
&& exception->mergeable(match)
&& match->mergeablePts(exception, missing_pt, match_missing_pt)) {
debugPrint(debug_, "exception_merge", 1, "merge %s",
exception->asString(network_));
debugPrint(debug_, "exception_merge", 1, " with %s",
match->asString(network_));
debugPrint(debug_, "exception_merge", 1, "merge {}",
exception->to_string(network_));
debugPrint(debug_, "exception_merge", 1, " with {}",
match->to_string(network_));
// Unrecord the exception that is being merged away.
unrecordException(exception);
unrecordMergeHashes(match);
@ -4965,8 +4965,8 @@ Sdc::deleteExceptionsReferencing(Clock *clk)
void
Sdc::deleteException(ExceptionPath *exception)
{
debugPrint(debug_, "exception_merge", 2, "delete %s",
exception->asString(network_));
debugPrint(debug_, "exception_merge", 2, "delete {}",
exception->to_string(network_));
unrecordException(exception);
delete exception;
}
@ -4996,10 +4996,10 @@ Sdc::unrecordMergeHash(ExceptionPath *exception,
{
size_t hash = exception->hash(missing_pt);
debugPrint(debug_, "exception_merge", 3,
"unrecord merge hash %zu %s missing %s",
"unrecord merge hash {} {} missing {}",
hash,
exception->asString(network_),
missing_pt->asString(network_));
exception->to_string(network_),
missing_pt->to_string(network_));
auto itr = exception_merge_hash_.find(hash);
if (itr != exception_merge_hash_.end()) {
ExceptionPathSet &matches = itr->second;
@ -5214,8 +5214,8 @@ Sdc::resetPath(ExceptionFrom *from,
for (auto itr = exceptions_.begin(); itr != exceptions_.end(); ) {
ExceptionPath *match = *itr;
if (match->resetMatch(from, thrus, to, min_max, network_)) {
debugPrint(debug_, "exception_match", 3, "reset match %s",
match->asString(network_));
debugPrint(debug_, "exception_match", 3, "reset match {}",
match->to_string(network_));
ExceptionPathSet expansions;
expandException(match, expansions);
itr = exceptions_.erase(itr);

View File

@ -860,8 +860,6 @@ make_group_path(const char *name,
{
Sta *sta = Sta::sta();
Sdc *sdc = sta->cmdSdc();
if (name[0] == '\0')
name = nullptr;
sta->makeGroupPath(name, is_default, from, thrus, to, comment, sdc);
}
@ -975,6 +973,14 @@ clock_groups_make_group(ClockGroups *clk_groups,
sta->makeClockGroup(clk_groups, clks, sdc);
}
void
unset_clock_groups_logically_exclusive()
{
Sta *sta = Sta::sta();
Sdc *sdc = sta->cmdSdc();
sta->removeClockGroupsLogicallyExclusive(sdc);
}
void
unset_clock_groups_logically_exclusive(const char *name)
{
@ -983,6 +989,14 @@ unset_clock_groups_logically_exclusive(const char *name)
sta->removeClockGroupsLogicallyExclusive(name, sdc);
}
void
unset_clock_groups_physically_exclusive()
{
Sta *sta = Sta::sta();
Sdc *sdc = sta->cmdSdc();
sta->removeClockGroupsPhysicallyExclusive(sdc);
}
void
unset_clock_groups_physically_exclusive(const char *name)
{
@ -991,6 +1005,14 @@ unset_clock_groups_physically_exclusive(const char *name)
sta->removeClockGroupsPhysicallyExclusive(name, sdc);
}
void
unset_clock_groups_asynchronous()
{
Sta *sta = Sta::sta();
Sdc *sdc = sta->cmdSdc();
sta->removeClockGroupsAsynchronous(sdc);
}
void
unset_clock_groups_asynchronous(const char *name)
{

View File

@ -1435,11 +1435,11 @@ proc unset_clk_groups_cmd { cmd cmd_args } {
if { $all } {
if { $logically_exclusive } {
unset_clock_groups_logically_exclusive "NULL"
unset_clock_groups_logically_exclusive_all
} elseif { $physically_exclusive } {
unset_clock_groups_physically_exclusive "NULL"
unset_clock_groups_physically_exclusive_all
} elseif { $asynchronous } {
unset_clock_groups_asynchronous "NULL"
unset_clock_groups_asynchronous_all
}
} else {
foreach name $names {

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "sdf/ReportAnnotation.hh"
@ -72,7 +72,8 @@ protected:
count_input_net,
count_output_net,
};
static const int count_index_max = static_cast<int>(CountIndex::count_output_net) + 1;
static const int count_index_max =
static_cast<int>(CountIndex::count_output_net) + 1;
static int count_delay;
void init();
@ -81,7 +82,7 @@ protected:
void reportDelayCounts();
void reportCheckCounts();
void reportArcs();
void reportArcs(const char *header,
void reportArcs(const std::string &header,
bool report_annotated,
PinSet &pins);
void reportArcs(Vertex *vertex,
@ -117,7 +118,6 @@ protected:
PinSet annotated_pins_;
};
int ReportAnnotated::count_delay;
void
@ -132,10 +132,9 @@ reportAnnotatedDelay(const Scene *scene,
bool report_constant_arcs,
StaState *sta)
{
ReportAnnotated report(scene, report_cells, report_nets,
from_in_ports, to_out_ports,
max_lines, report_annotated, report_unannotated,
report_constant_arcs, sta);
ReportAnnotated report(scene, report_cells, report_nets, from_in_ports,
to_out_ports, max_lines, report_annotated,
report_unannotated, report_constant_arcs, sta);
report.reportDelayAnnotation();
}
@ -176,24 +175,27 @@ ReportAnnotated::reportDelayAnnotation()
void
ReportAnnotated::reportDelayCounts()
{
report_->reportLine(" Not ");
report_->reportLine("Delay type Total Annotated Annotated");
report_->reportLine("----------------------------------------------------------------");
report_->report(
" Not ");
report_->report(
"Delay type Total Annotated Annotated");
report_->report(
"----------------------------------------------------------------");
int total = 0;
int annotated_total = 0;
reportCount("cell arcs", count_delay, total, annotated_total);
reportCount("internal net arcs", static_cast<int>(CountIndex::count_internal_net), total, annotated_total);
reportCount("net arcs from primary inputs", static_cast<int>(CountIndex::count_input_net),
total, annotated_total);
reportCount("net arcs to primary outputs", static_cast<int>(CountIndex::count_output_net),
reportCount("internal net arcs", static_cast<int>(CountIndex::count_internal_net),
total, annotated_total);
report_->reportLine("----------------------------------------------------------------");
report_->reportLine("%-28s %10u %10u %10u",
" ",
total,
annotated_total,
total - annotated_total);
reportCount("net arcs from primary inputs",
static_cast<int>(CountIndex::count_input_net), total, annotated_total);
reportCount("net arcs to primary outputs",
static_cast<int>(CountIndex::count_output_net), total,
annotated_total);
report_->report(
"----------------------------------------------------------------");
report_->report("{:<28} {:10} {:10} {:10}", " ", total, annotated_total,
total - annotated_total);
}
////////////////////////////////////////////////////////////////
@ -215,12 +217,10 @@ reportAnnotatedCheck(const Scene *scene,
StaState *sta)
{
ReportAnnotated report(scene, report_setup, report_hold,
report_recovery, report_removal,
report_nochange, report_width,
report_period, report_max_skew,
max_lines, report_annotated, report_unannotated,
report_constant_arcs, sta);
ReportAnnotated report(scene, report_setup, report_hold, report_recovery,
report_removal, report_nochange, report_width,
report_period, report_max_skew, max_lines, report_annotated,
report_unannotated, report_constant_arcs, sta);
report.reportCheckAnnotation();
}
@ -269,9 +269,12 @@ ReportAnnotated::reportCheckAnnotation()
void
ReportAnnotated::reportCheckCounts()
{
report_->reportLine(" Not ");
report_->reportLine("Check type Total Annotated Annotated");
report_->reportLine("----------------------------------------------------------------");
report_->report(
" Not ");
report_->report(
"Check type Total Annotated Annotated");
report_->report(
"----------------------------------------------------------------");
int total = 0;
int annotated_total = 0;
@ -284,12 +287,10 @@ ReportAnnotated::reportCheckCounts()
reportCheckCount(TimingRole::period(), total, annotated_total);
reportCheckCount(TimingRole::skew(), total, annotated_total);
report_->reportLine("----------------------------------------------------------------");
report_->reportLine("%-28s %10u %10u %10u",
" ",
total,
annotated_total,
total - annotated_total);
report_->report(
"----------------------------------------------------------------");
report_->report("{:<28} {:10} {:10} {:10}", " ", total, annotated_total,
total - annotated_total);
}
void
@ -299,8 +300,7 @@ ReportAnnotated::reportCheckCount(const TimingRole *role,
{
int index = role->index();
if (edge_count_[index] > 0) {
std::string title;
stringPrint(title, "cell %s arcs", role->to_string().c_str());
std::string title = sta::format("cell {} arcs", role->to_string());
reportCount(title.c_str(), index, total, annotated_total);
}
}
@ -330,8 +330,7 @@ ReportAnnotated::findCounts()
Pin *from_pin = from_vertex->pin();
LogicValue from_logic_value;
bool from_logic_value_exists;
sdc->logicValue(from_pin, from_logic_value,
from_logic_value_exists);
sdc->logicValue(from_pin, from_logic_value, from_logic_value_exists);
VertexOutEdgeIterator edge_iter(from_vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
@ -341,8 +340,7 @@ ReportAnnotated::findCounts()
int index = roleIndex(role, from_pin, to_pin);
LogicValue to_logic_value;
bool to_logic_value_exists;
sdc->logicValue(to_pin, to_logic_value,
to_logic_value_exists);
sdc->logicValue(to_pin, to_logic_value, to_logic_value_exists);
edge_count_[index]++;
@ -374,7 +372,7 @@ ReportAnnotated::delayAnnotated(Edge *edge)
for (const MinMax *min_max : MinMax::range()) {
DcalcAPIndex ap_index = scene_->dcalcAnalysisPtIndex(min_max);
if (!graph_->arcDelayAnnotated(edge, arc, ap_index))
return false;
return false;
}
}
return true;
@ -397,8 +395,7 @@ ReportAnnotated::roleIndex(const TimingRole *role,
return count_delay;
else {
if (role->isTimingCheck()
&& (role == TimingRole::latchSetup()
|| role == TimingRole::latchHold()))
&& (role == TimingRole::latchSetup() || role == TimingRole::latchHold()))
role = role->genericRole();
return role->index();
}
@ -443,19 +440,13 @@ ReportAnnotated::reportCount(const char *title,
if (report_role_[index]) {
int count = edge_count_[index];
int annotated_count = edge_annotated_count_[index];
report_->reportLine("%-28s %10u %10u %10u",
title,
count,
annotated_count,
count - annotated_count);
report_->report("{:<28} {:10} {:10} {:10}", title, count, annotated_count,
count - annotated_count);
if (report_constant_arcs_) {
int const_count = edge_constant_count_[index];
int const_annotated_count = edge_constant_annotated_count_[index];
report_->reportLine("%-28s %10s %10u %10u",
"constant arcs",
"",
const_annotated_count,
const_count - const_annotated_count);
report_->report("{:<28} {:10} {:10} {:10}", "constant arcs", "",
const_annotated_count, const_count - const_annotated_count);
}
total += count;
annotated_total += annotated_count;
@ -472,12 +463,12 @@ ReportAnnotated::reportArcs()
}
void
ReportAnnotated::reportArcs(const char *header,
ReportAnnotated::reportArcs(const std::string &header,
bool report_annotated,
PinSet &pins)
{
report_->reportBlankLine();
report_->reportLineString(header);
report_->reportLine(header);
PinSeq pins1 = sortByPathName(&pins, network_);
int i = 0;
for (const Pin *pin : pins1) {
@ -499,8 +490,7 @@ ReportAnnotated::reportArcs(Vertex *vertex,
{
const Pin *from_pin = vertex->pin();
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()
&& (max_lines_ == 0 || i < max_lines_)) {
while (edge_iter.hasNext() && (max_lines_ == 0 || i < max_lines_)) {
Edge *edge = edge_iter.next();
const TimingRole *role = edge->role();
const Pin *to_pin = edge->to(graph_)->pin();
@ -520,11 +510,8 @@ ReportAnnotated::reportArcs(Vertex *vertex,
else
role_name = "delay";
const std::string &cond = edge->timingArcSet()->sdfCond();
report_->reportLine(" %-18s %s -> %s %s",
role_name,
network_->pathName(from_pin),
network_->pathName(to_pin),
cond.c_str());
report_->report(" {:<18} {} -> {} {}", role_name, network_->pathName(from_pin),
network_->pathName(to_pin), cond);
i++;
}
}
@ -539,8 +526,7 @@ ReportAnnotated::reportPeriodArcs(const Pin *pin,
if (port) {
DcalcAPIndex ap_index = 0;
int period_index = TimingRole::period()->index();
if (report_role_[period_index]
&& (max_lines_ == 0 || i < max_lines_)) {
if (report_role_[period_index] && (max_lines_ == 0 || i < max_lines_)) {
float value;
bool exists, annotated;
port->minPeriod(value, exists);
@ -548,9 +534,7 @@ ReportAnnotated::reportPeriodArcs(const Pin *pin,
edge_count_[period_index]++;
graph_->periodCheckAnnotation(pin, ap_index, value, annotated);
if (annotated == report_annotated) {
report_->reportLine(" %-18s %s",
"period",
network_->pathName(pin));
report_->report(" {:<18} {}", "period", network_->pathName(pin));
i++;
}
}
@ -558,4 +542,4 @@ ReportAnnotated::reportPeriodArcs(const Pin *pin,
}
}
} // namespace
} // namespace sta

View File

@ -38,8 +38,7 @@ void
sta::SdfParse::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(170, reader->filename(), loc.begin.line,"{}",msg);
}
%}

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "sdf/SdfReader.hh"
@ -74,7 +74,7 @@ public:
private:
const Transition *tr_;
const std::string *port_;
const std::string *cond_; // timing checks only
const std::string *cond_; // timing checks only
};
bool
@ -88,11 +88,9 @@ readSdf(const char *filename,
{
int arc_min_index = scene->dcalcAnalysisPtIndex(MinMax::min());
int arc_max_index = scene->dcalcAnalysisPtIndex(MinMax::max());
SdfReader reader(filename, path,
arc_min_index, arc_max_index,
scene->sdc()->analysisType(),
unescaped_dividers, incremental_only,
cond_use, sta);
SdfReader reader(filename, path, arc_min_index, arc_max_index,
scene->sdc()->analysisType(), unescaped_dividers,
incremental_only, cond_use, sta);
bool success = reader.read();
return success;
}
@ -123,7 +121,7 @@ SdfReader::SdfReader(const char *filename,
cell_name_(nullptr),
in_timing_check_(false),
in_incremental_(false),
timescale_(1.0E-9F) // default units of ns
timescale_(1.0E-9F) // default units of ns
{
if (unescaped_dividers)
network_ = makeSdcNetwork(network_);
@ -149,7 +147,7 @@ SdfReader::read()
return success;
}
else
throw FileNotReadable(filename_.c_str());
throw FileNotReadable(filename_);
}
void
@ -162,9 +160,7 @@ void
SdfReader::setTimescale(float multiplier,
const std::string *units)
{
if (multiplier == 1.0
|| multiplier == 10.0
|| multiplier == 100.0) {
if (multiplier == 1.0 || multiplier == 10.0 || multiplier == 100.0) {
if (*units == "us")
timescale_ = multiplier * 1E-6F;
else if (*units == "ns")
@ -172,10 +168,10 @@ SdfReader::setTimescale(float multiplier,
else if (*units == "ps")
timescale_ = multiplier * 1E-12F;
else
sdfError(180, "TIMESCALE units not us, ns, or ps.");
error(180, "TIMESCALE units not us, ns, or ps.");
}
else
sdfError(181, "TIMESCALE multiplier not 1, 10, or 100.");
error(181, "TIMESCALE multiplier not 1, 10, or 100.");
delete units;
}
@ -198,23 +194,20 @@ SdfReader::interconnect(const std::string *from_pin_name,
bool to_is_hier = network_->isHierarchical(to_pin);
if (from_is_hier || to_is_hier) {
if (from_is_hier)
sdfError(182, "pin %s is a hierarchical pin.",
from_pin_name->c_str());
error(182, "pin {} is a hierarchical pin.", *from_pin_name);
if (to_is_hier)
sdfError(183, "pin %s is a hierarchical pin.",
to_pin_name->c_str());
error(183, "pin {} is a hierarchical pin.", *to_pin_name);
}
else
sdfWarn(184, "INTERCONNECT from %s to %s not found.",
from_pin_name->c_str(),
to_pin_name->c_str());
warn(184, "INTERCONNECT from {} to {} not found.",
*from_pin_name, *to_pin_name);
}
}
else {
if (from_pin == nullptr)
sdfWarn(185, "pin %s not found.", from_pin_name->c_str());
warn(185, "pin {} not found.", *from_pin_name);
if (to_pin == nullptr)
sdfWarn(186, "pin %s not found.", to_pin_name->c_str());
warn(186, "pin {} not found.", *to_pin_name);
}
}
delete from_pin_name;
@ -229,10 +222,10 @@ SdfReader::port(const std::string *to_pin_name,
// Ignore non-incremental annotations in incremental only mode.
if (!(is_incremental_only_ && !in_incremental_)) {
Pin *to_pin = (instance_)
? network_->findPinRelative(instance_, to_pin_name->c_str())
: network_->findPin(to_pin_name->c_str());
? network_->findPinRelative(instance_, to_pin_name->c_str())
: network_->findPin(to_pin_name->c_str());
if (to_pin == nullptr)
sdfWarn(187, "pin %s not found.", to_pin_name->c_str());
warn(187, "pin {} not found.", *to_pin_name);
else {
Vertex *vertex = graph_->pinLoadVertex(to_pin);
VertexInEdgeIterator edge_iter(vertex, graph_);
@ -259,8 +252,7 @@ SdfReader::findWireEdge(Pin *from_pin,
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
const TimingRole *edge_role = edge->role();
if (edge->from(graph_)->pin() == from_pin
&& edge_role->sdfRole()->isWire())
if (edge->from(graph_)->pin() == from_pin && edge_role->sdfRole()->isWire())
return edge;
}
}
@ -274,8 +266,7 @@ SdfReader::setEdgeDelays(Edge *edge,
{
// Rise/fall triples.
size_t triple_count = triples->size();
if (triple_count == 1
|| triple_count == 2) {
if (triple_count == 1 || triple_count == 2) {
TimingArcSet *arc_set = edge->timingArcSet();
for (TimingArc *arc : arc_set->arcs()) {
size_t triple_index;
@ -288,9 +279,9 @@ SdfReader::setEdgeDelays(Edge *edge,
}
}
else if (triple_count == 0)
sdfError(188, "%s with no triples.", sdf_cmd);
error(188, "{} with no triples.", sdf_cmd);
else
sdfError(189, "%s with more than 2 triples.", sdf_cmd);
error(189, "{} with more than 2 triples.", sdf_cmd);
}
void
@ -313,10 +304,8 @@ SdfReader::setInstance(const std::string *instance_name)
Cell *inst_cell = network_->cell(instance_);
const char *inst_cell_name = network_->name(inst_cell);
if (cell_name_ && !stringEq(inst_cell_name, cell_name_->c_str()))
sdfWarn(190, "instance %s cell %s does not match enclosing cell %s.",
instance_name->c_str(),
inst_cell_name,
cell_name_->c_str());
warn(190, "instance {} cell {} does not match enclosing cell {}.",
*instance_name, inst_cell_name, *cell_name_);
}
}
}
@ -372,7 +361,7 @@ SdfReader::iopath(SdfPortSpec *from_edge,
const std::string &lib_cond = arc_set->sdfCond();
const TimingRole *edge_role = arc_set->role();
bool cond_use_flag = cond_use_ && cond && lib_cond.empty()
&& !(!is_incremental_only_ && in_incremental_);
&& !(!is_incremental_only_ && in_incremental_);
if (edge->from(graph_)->pin() == from_pin
&& edge_role->sdfRole() == TimingRole::sdfIopath()
&& (cond_use_flag
@ -402,10 +391,9 @@ SdfReader::iopath(SdfPortSpec *from_edge,
}
}
if (!matched)
sdfWarn(191, "cell %s IOPATH %s -> %s not found.",
network_->cellName(instance_),
from_port_name->c_str(),
to_port_name->c_str());
warn(191, "cell {} IOPATH {} -> {} not found.",
network_->cellName(instance_), *from_port_name,
*to_port_name);
}
}
}
@ -422,9 +410,8 @@ SdfReader::findPort(const Cell *cell,
{
Port *port = network_->findPort(cell, port_name->c_str());
if (port == nullptr)
sdfWarn(194, "instance %s port %s not found.",
network_->pathName(instance_),
port_name->c_str());
warn(194, "instance {} port {} not found.", network_->pathName(instance_),
*port_name);
return port;
}
@ -457,8 +444,7 @@ SdfReader::timingCheck1(const TimingRole *role,
SdfTriple *triple)
{
// Ignore non-incremental annotations in incremental only mode.
if (!(is_incremental_only_ && !in_incremental_)
&& instance_) {
if (!(is_incremental_only_ && !in_incremental_) && instance_) {
Pin *data_pin = network_->findPin(instance_, data_port);
Pin *clk_pin = network_->findPin(instance_, clk_port);
if (data_pin && clk_pin) {
@ -468,36 +454,32 @@ SdfReader::timingCheck1(const TimingRole *role,
float *value_max = values[triple_max_index_];
if (value_min && value_max) {
switch (analysis_type_) {
case AnalysisType::single:
break;
case AnalysisType::bc_wc:
if (role->genericRole() == TimingRole::setup())
case AnalysisType::single:
break;
case AnalysisType::bc_wc:
if (role->genericRole() == TimingRole::setup())
*value_min = *value_max;
else
*value_max = *value_min;
break;
case AnalysisType::ocv:
*value_min = *value_max;
else
*value_max = *value_min;
break;
case AnalysisType::ocv:
*value_min = *value_max;
break;
break;
}
}
bool matched = annotateCheckEdges(data_pin, data_edge,
clk_pin, clk_edge, role,
bool matched = annotateCheckEdges(data_pin, data_edge, clk_pin, clk_edge, role,
triple, false);
// Liberty setup/hold checks on preset/clear pins can be translated
// into recovery/removal checks, so be flexible about matching.
if (!matched)
matched = annotateCheckEdges(data_pin, data_edge,
clk_pin, clk_edge, role,
matched = annotateCheckEdges(data_pin, data_edge, clk_pin, clk_edge, role,
triple, true);
if (!matched
// Only warn when non-null values are present.
&& triple->hasValue())
sdfWarn(192, "cell %s %s -> %s %s check not found.",
network_->cellName(instance_),
network_->name(data_port),
network_->name(clk_port),
role->to_string().c_str());
warn(192, "cell {} {} -> {} {} check not found.",
network_->cellName(instance_), network_->name(data_port),
network_->name(clk_port), role->to_string());
}
}
}
@ -526,11 +508,10 @@ SdfReader::annotateCheckEdges(Pin *data_pin,
const TimingRole *edge_role = arc_set->role();
const std::string &lib_cond_start = arc_set->sdfCondStart();
const std::string &lib_cond_end = arc_set->sdfCondEnd();
bool cond_matches = condMatch(cond_start, lib_cond_start)
&& condMatch(cond_end, lib_cond_end);
bool cond_matches =
condMatch(cond_start, lib_cond_start) && condMatch(cond_end, lib_cond_end);
if (((!match_generic && edge_role->sdfRole() == sdf_role)
|| (match_generic
&& edge_role->genericRole() == sdf_role->genericRole()))
|| (match_generic && edge_role->genericRole() == sdf_role->genericRole()))
&& cond_matches) {
TimingArcSet *arc_set = edge->timingArcSet();
for (TimingArc *arc : arc_set->arcs()) {
@ -553,8 +534,7 @@ SdfReader::timingCheckWidth(SdfPortSpec *edge,
SdfTriple *triple)
{
// Ignore non-incremental annotations in incremental only mode.
if (!(is_incremental_only_ && !in_incremental_)
&& instance_) {
if (!(is_incremental_only_ && !in_incremental_) && instance_) {
const std::string *port_name = edge->port();
Cell *cell = network_->cell(instance_);
Port *port = findPort(cell, port_name);
@ -622,8 +602,7 @@ SdfReader::timingCheckPeriod(SdfPortSpec *edge,
SdfTriple *triple)
{
// Ignore non-incremental annotations in incremental only mode.
if (!(is_incremental_only_ && !in_incremental_)
&& instance_) {
if (!(is_incremental_only_ && !in_incremental_) && instance_) {
const std::string *port_name = edge->port();
Cell *cell = network_->cell(instance_);
Port *port = findPort(cell, port_name);
@ -668,8 +647,7 @@ void
SdfReader::device(SdfTripleSeq *triples)
{
// Ignore non-incremental annotations in incremental only mode.
if (!(is_incremental_only_ && !in_incremental_)
&& instance_) {
if (!(is_incremental_only_ && !in_incremental_) && instance_) {
InstancePinIterator *pin_iter = network_->pinIterator(instance_);
while (pin_iter->hasNext()) {
Pin *to_pin = pin_iter->next();
@ -685,8 +663,7 @@ SdfReader::device(const std::string *to_port_name,
SdfTripleSeq *triples)
{
// Ignore non-incremental annotations in incremental only mode.
if (!(is_incremental_only_ && !in_incremental_)
&& instance_) {
if (!(is_incremental_only_ && !in_incremental_) && instance_) {
Cell *cell = network_->cell(instance_);
Port *to_port = findPort(cell, to_port_name);
if (to_port) {
@ -780,8 +757,7 @@ SdfReader::setEdgeArcDelaysCondUse(Edge *edge,
int arc_delay_index,
const MinMax *min_max)
{
if (value
&& triple_index != null_index_) {
if (value && triple_index != null_index_) {
ArcDelay delay(*value);
if (!is_incremental_only_ && in_incremental_)
delay = delaySum(graph_->arcDelay(edge, arc, arc_delay_index), *value, this);
@ -844,9 +820,7 @@ SdfReader::makeCondPortSpec(const std::string *cond_port)
auto cond_end = cond_port1.find_last_not_of(" ", port_idx);
if (cond_end != cond_port1.npos) {
std::string *cond1 = new std::string(cond_port1.substr(0, cond_end + 1));
SdfPortSpec *port_spec = new SdfPortSpec(Transition::riseFall(),
port1,
cond1);
SdfPortSpec *port_spec = new SdfPortSpec(Transition::riseFall(), port1, cond1);
delete cond_port;
return port_spec;
}
@ -887,9 +861,12 @@ SdfReader::makeTriple(float *min,
float *typ,
float *max)
{
if (min) *min *= timescale_;
if (typ) *typ *= timescale_;
if (max) *max *= timescale_;
if (min)
*min *= timescale_;
if (typ)
*typ *= timescale_;
if (max)
*max *= timescale_;
return new SdfTriple(min, typ, max);
}
@ -929,9 +906,7 @@ SdfReader::unescaped(const std::string *token)
// Translate sdf divider to network divider.
*unescaped += path_divider;
}
else if (next_ch == '['
|| next_ch == ']'
|| next_ch == escape_) {
else if (next_ch == '[' || next_ch == ']' || next_ch == escape_) {
// Escaped bus bracket or escape.
// Translate sdf escape to network escape.
*unescaped += path_escape;
@ -946,9 +921,8 @@ SdfReader::unescaped(const std::string *token)
// Just the normal noises.
*unescaped += ch;
}
debugPrint(debug_, "sdf_name", 1, "unescape %s -> %s",
token->c_str(),
unescaped->c_str());
debugPrint(debug_, "sdf_name", 1, "unescape {} -> {}", *token,
*unescaped);
delete token;
return unescaped;
}
@ -980,27 +954,13 @@ SdfReader::makeBusName(std::string *base_name,
void
SdfReader::notSupported(const char *feature)
{
sdfError(193, "%s not supported.", feature);
error(193, "{} not supported.", feature);
}
void
SdfReader::sdfWarn(int id,
const char *fmt, ...)
int
SdfReader::sdfLine() const
{
va_list args;
va_start(args, fmt);
report_->vfileWarn(id, filename_.c_str(), scanner_->lineno(), fmt, args);
va_end(args);
}
void
SdfReader::sdfError(int id,
const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
report_->vfileError(id, filename_.c_str(), scanner_->lineno(), fmt, args);
va_end(args);
return scanner_->lineno();
}
Pin *
@ -1030,7 +990,7 @@ SdfReader::findInstance(const std::string *name)
inst_name = *name;
Instance *inst = network_->findInstance(inst_name.c_str());
if (inst == nullptr)
sdfWarn(195, "instance %s not found.", inst_name.c_str());
warn(195, "instance {} not found.", inst_name);
return inst;
}
@ -1067,9 +1027,12 @@ SdfTriple::~SdfTriple()
if (values_[0] == values_[1] && values_[0] == values_[2])
delete values_[0];
else {
if (values_[0]) delete values_[0];
if (values_[1]) delete values_[1];
if (values_[2]) delete values_[2];
if (values_[0])
delete values_[0];
if (values_[1])
delete values_[1];
if (values_[2])
delete values_[2];
}
}
@ -1095,7 +1058,7 @@ SdfScanner::SdfScanner(std::istream *stream,
void
SdfScanner::error(const char *msg)
{
report_->fileError(1869, filename_.c_str(), lineno(), "%s", msg);
report_->fileError(196, filename_.c_str(), lineno(), "{}", msg);
}
} // namespace
} // namespace sta

View File

@ -24,6 +24,7 @@
#pragma once
#include <string_view>
#include <vector>
#include "TimingRole.hh"
@ -31,6 +32,7 @@
#include "LibertyClass.hh"
#include "NetworkClass.hh"
#include "GraphClass.hh"
#include "Report.hh"
#include "SdcClass.hh"
#include "StaState.hh"
@ -148,11 +150,23 @@ public:
std::string *makeBusName(std::string *bus_name,
int index);
const std::string &filename() const { return filename_; }
void sdfWarn(int id,
const char *fmt, ...);
void sdfError(int id,
const char *fmt,
...);
int sdfLine() const;
template <typename... Args>
void warn(int id,
std::string_view fmt,
Args &&...args)
{
report_->fileWarn(id, filename_, sdfLine(), fmt,
std::forward<Args>(args)...);
}
template <typename... Args>
void error(int id,
std::string_view fmt,
Args &&...args)
{
report_->fileError(id, filename_, sdfLine(), fmt,
std::forward<Args>(args)...);
}
void notSupported(const char *feature);
private:

View File

@ -27,6 +27,7 @@
#include <cstdio>
#include <ctime>
#include "Format.hh"
#include "Zlib.hh"
#include "StaConfig.hh" // STA_VERSION
#include "Fuzzy.hh"
@ -49,7 +50,6 @@ class SdfWriter : public StaState
{
public:
SdfWriter(StaState *sta);
~SdfWriter();
void write(const char *filename,
const Scene *scene,
char sdf_divider,
@ -118,7 +118,7 @@ private:
char sdf_escape_;
char network_escape_;
char *delay_format_;
int digits_;
gzFile stream_;
const Scene *scene_;
@ -145,16 +145,10 @@ writeSdf(const char *filename,
SdfWriter::SdfWriter(StaState *sta) :
StaState(sta),
sdf_escape_('\\'),
network_escape_(network_->pathEscape()),
delay_format_(nullptr)
network_escape_(network_->pathEscape())
{
}
SdfWriter::~SdfWriter()
{
stringDelete(delay_format_);
}
void
SdfWriter::write(const char *filename,
const Scene *scene,
@ -167,8 +161,7 @@ SdfWriter::write(const char *filename,
{
sdf_divider_ = sdf_divider;
include_typ_ = include_typ;
if (delay_format_ == nullptr)
delay_format_ = stringPrint("%%.%df", digits);
digits_ = digits;
LibertyLibrary *default_lib = network_->defaultLibertyLibrary();
timescale_ = default_lib->units()->timeUnit()->scale();
@ -195,25 +188,25 @@ SdfWriter::writeHeader(LibertyLibrary *default_lib,
bool no_timestamp,
bool no_version)
{
gzprintf(stream_, "(DELAYFILE\n");
gzprintf(stream_, " (SDFVERSION \"3.0\")\n");
gzprintf(stream_, " (DESIGN \"%s\")\n",
network_->cellName(network_->topInstance()));
sta::print(stream_, "(DELAYFILE\n");
sta::print(stream_, " (SDFVERSION \"3.0\")\n");
sta::print(stream_, " (DESIGN \"{}\")\n",
network_->cellName(network_->topInstance()));
if (!no_timestamp) {
time_t now;
time(&now);
char *time_str = ctime(&now);
// Remove trailing \n.
time_str[strlen(time_str) - 1] = '\0';
gzprintf(stream_, " (DATE \"%s\")\n", time_str);
sta::print(stream_, " (DATE \"{}\")\n", time_str);
}
gzprintf(stream_, " (VENDOR \"Parallax\")\n");
gzprintf(stream_, " (PROGRAM \"STA\")\n");
sta::print(stream_, " (VENDOR \"Parallax\")\n");
sta::print(stream_, " (PROGRAM \"STA\")\n");
if (!no_version)
gzprintf(stream_, " (VERSION \"%s\")\n", STA_VERSION);
gzprintf(stream_, " (DIVIDER %c)\n", sdf_divider_);
sta::print(stream_, " (VERSION \"{}\")\n", STA_VERSION);
sta::print(stream_, " (DIVIDER {:c})\n", sdf_divider_);
LibertyLibrary *lib_min = default_lib;
const LibertySeq &libs_min = scene_->libertyLibraries(MinMax::min());
@ -227,15 +220,15 @@ SdfWriter::writeHeader(LibertyLibrary *default_lib,
OperatingConditions *cond_min = lib_min->defaultOperatingConditions();
OperatingConditions *cond_max = lib_max->defaultOperatingConditions();
if (cond_min && cond_max) {
gzprintf(stream_, " (VOLTAGE %.3f::%.3f)\n",
cond_min->voltage(),
cond_max->voltage());
gzprintf(stream_, " (PROCESS \"%.3f::%.3f\")\n",
cond_min->process(),
cond_max->process());
gzprintf(stream_, " (TEMPERATURE %.3f::%.3f)\n",
cond_min->temperature(),
cond_max->temperature());
sta::print(stream_, " (VOLTAGE {:.3f}::{:.3f})\n",
cond_min->voltage(),
cond_max->voltage());
sta::print(stream_, " (PROCESS \"{:.3f}::{:.3f}\")\n",
cond_min->process(),
cond_max->process());
sta::print(stream_, " (TEMPERATURE {:.3f}::{:.3f})\n",
cond_min->temperature(),
cond_max->temperature());
}
const char *sdf_timescale = nullptr;
@ -258,24 +251,24 @@ SdfWriter::writeHeader(LibertyLibrary *default_lib,
else if (fuzzyEqual(timescale_, 100e-12))
sdf_timescale = "100ps";
if (sdf_timescale)
gzprintf(stream_, " (TIMESCALE %s)\n", sdf_timescale);
sta::print(stream_, " (TIMESCALE {})\n", sdf_timescale);
}
void
SdfWriter::writeTrailer()
{
gzprintf(stream_, ")\n");
sta::print(stream_, ")\n");
}
void
SdfWriter::writeInterconnects()
{
gzprintf(stream_, " (CELL\n");
gzprintf(stream_, " (CELLTYPE \"%s\")\n",
network_->cellName(network_->topInstance()));
gzprintf(stream_, " (INSTANCE)\n");
gzprintf(stream_, " (DELAY\n");
gzprintf(stream_, " (ABSOLUTE\n");
sta::print(stream_, " (CELL\n");
sta::print(stream_, " (CELLTYPE \"{}\")\n",
network_->cellName(network_->topInstance()));
sta::print(stream_, " (INSTANCE)\n");
sta::print(stream_, " (DELAY\n");
sta::print(stream_, " (ABSOLUTE\n");
writeInstInterconnects(network_->topInstance());
@ -286,9 +279,9 @@ SdfWriter::writeInterconnects()
}
delete inst_iter;
gzprintf(stream_, " )\n");
gzprintf(stream_, " )\n");
gzprintf(stream_, " )\n");
sta::print(stream_, " )\n");
sta::print(stream_, " )\n");
sta::print(stream_, " )\n");
}
void
@ -315,11 +308,11 @@ SdfWriter::writeInterconnectFromPin(Pin *drvr_pin)
Pin *load_pin = edge->to(graph_)->pin();
std::string drvr_pin_name = sdfPathName(drvr_pin);
std::string load_pin_name = sdfPathName(load_pin);
gzprintf(stream_, " (INTERCONNECT %s %s ",
drvr_pin_name.c_str(),
load_pin_name.c_str());
sta::print(stream_, " (INTERCONNECT {} {} ",
drvr_pin_name,
load_pin_name);
writeArcDelays(edge);
gzprintf(stream_, ")\n");
sta::print(stream_, ")\n");
}
}
}
@ -343,16 +336,16 @@ SdfWriter::writeInstances()
void
SdfWriter::writeInstHeader(const Instance *inst)
{
gzprintf(stream_, " (CELL\n");
gzprintf(stream_, " (CELLTYPE \"%s\")\n", network_->cellName(inst));
sta::print(stream_, " (CELL\n");
sta::print(stream_, " (CELLTYPE \"{}\")\n", network_->cellName(inst));
std::string inst_name = sdfPathName(inst);
gzprintf(stream_, " (INSTANCE %s)\n", inst_name.c_str());
sta::print(stream_, " (INSTANCE {})\n", inst_name);
}
void
SdfWriter::writeInstTrailer()
{
gzprintf(stream_, " )\n");
sta::print(stream_, " )\n");
}
void
@ -387,18 +380,18 @@ SdfWriter::writeIopaths(const Instance *inst,
}
const std::string &sdf_cond = edge->timingArcSet()->sdfCond();
if (!sdf_cond.empty()) {
gzprintf(stream_, " (COND %s\n", sdf_cond.c_str());
gzprintf(stream_, " ");
sta::print(stream_, " (COND {}\n", sdf_cond);
sta::print(stream_, " ");
}
std::string from_pin_name = sdfPortName(from_pin);
std::string to_pin_name = sdfPortName(to_pin);
gzprintf(stream_, " (IOPATH %s %s ",
from_pin_name.c_str(),
to_pin_name.c_str());
sta::print(stream_, " (IOPATH {} {} ",
from_pin_name,
to_pin_name);
writeArcDelays(edge);
if (!sdf_cond.empty())
gzprintf(stream_, ")");
gzprintf(stream_, ")\n");
sta::print(stream_, ")");
sta::print(stream_, ")\n");
}
}
}
@ -412,15 +405,15 @@ SdfWriter::writeIopaths(const Instance *inst,
void
SdfWriter::writeIopathHeader()
{
gzprintf(stream_, " (DELAY\n");
gzprintf(stream_, " (ABSOLUTE\n");
sta::print(stream_, " (DELAY\n");
sta::print(stream_, " (ABSOLUTE\n");
}
void
SdfWriter::writeIopathTrailer()
{
gzprintf(stream_, " )\n");
gzprintf(stream_, " )\n");
sta::print(stream_, " )\n");
sta::print(stream_, " )\n");
}
void
@ -446,7 +439,7 @@ SdfWriter::writeArcDelays(Edge *edge)
delays.value(RiseFall::fall(), MinMax::min()))
&& fuzzyEqual(delays.value(RiseFall::rise(), MinMax::max()),
delays.value(RiseFall::fall(),MinMax::max())))) {
gzprintf(stream_, " ");
sta::print(stream_, " ");
writeSdfTriple(delays, RiseFall::fall());
}
}
@ -455,7 +448,7 @@ SdfWriter::writeArcDelays(Edge *edge)
writeSdfTriple(delays, RiseFall::rise());
else if (delays.hasValue(RiseFall::fall(), MinMax::min())) {
// Fall only.
gzprintf(stream_, "() ");
sta::print(stream_, "() ");
writeSdfTriple(delays, RiseFall::fall());
}
}
@ -473,23 +466,24 @@ void
SdfWriter::writeSdfTriple(float min,
float max)
{
gzprintf(stream_, "(");
sta::print(stream_, "(");
writeSdfDelay(min);
if (include_typ_) {
gzprintf(stream_, ":");
sta::print(stream_, ":");
writeSdfDelay((min + max) / 2.0);
gzprintf(stream_, ":");
sta::print(stream_, ":");
}
else
gzprintf(stream_, "::");
sta::print(stream_, "::");
writeSdfDelay(max);
gzprintf(stream_, ")");
sta::print(stream_, ")");
}
void
SdfWriter::writeSdfDelay(double delay)
{
gzprintf(stream_, delay_format_, delay / timescale_);
std::string str = sta::formatRuntime("{:.{}f}", delay / timescale_, digits_);
sta::print(stream_, "{}", str);
}
void
@ -568,13 +562,13 @@ SdfWriter::ensureTimingCheckheaders(bool &check_header,
void
SdfWriter::writeTimingCheckHeader()
{
gzprintf(stream_, " (TIMINGCHECK\n");
sta::print(stream_, " (TIMINGCHECK\n");
}
void
SdfWriter::writeTimingCheckTrailer()
{
gzprintf(stream_, " )\n");
sta::print(stream_, " )\n");
}
void
@ -663,40 +657,40 @@ SdfWriter::writeCheck(Edge *edge,
const std::string &sdf_cond_start = arc_set->sdfCondStart();
const std::string &sdf_cond_end = arc_set->sdfCondEnd();
gzprintf(stream_, " (%s ", sdf_check);
sta::print(stream_, " ({} ", sdf_check);
if (!sdf_cond_start.empty())
gzprintf(stream_, "(COND %s ", sdf_cond_start.c_str());
sta::print(stream_, "(COND {} ", sdf_cond_start);
std::string to_pin_name = sdfPortName(to_pin);
if (use_data_edge) {
gzprintf(stream_, "(%s %s)",
sdfEdge(arc->toEdge()),
to_pin_name.c_str());
sta::print(stream_, "({} {})",
sdfEdge(arc->toEdge()),
to_pin_name);
}
else
gzprintf(stream_, "%s", to_pin_name.c_str());
sta::print(stream_, "{}", to_pin_name);
if (!sdf_cond_start.empty())
gzprintf(stream_, ")");
sta::print(stream_, ")");
gzprintf(stream_, " ");
sta::print(stream_, " ");
if (!sdf_cond_end.empty())
gzprintf(stream_, "(COND %s ", sdf_cond_end.c_str());
sta::print(stream_, "(COND {} ", sdf_cond_end);
std::string from_pin_name = sdfPortName(from_pin);
if (use_clk_edge)
gzprintf(stream_, "(%s %s)",
sdfEdge(arc->fromEdge()),
from_pin_name.c_str());
sta::print(stream_, "({} {})",
sdfEdge(arc->fromEdge()),
from_pin_name);
else
gzprintf(stream_, "%s", from_pin_name.c_str());
sta::print(stream_, "{}", from_pin_name);
if (!sdf_cond_end.empty())
gzprintf(stream_, ")");
sta::print(stream_, ")");
gzprintf(stream_, " ");
sta::print(stream_, " ");
float min_delay = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_),
MinMax::min(), this);
@ -704,7 +698,7 @@ SdfWriter::writeCheck(Edge *edge,
MinMax::max(), this);
writeSdfTriple(min_delay, max_delay);
gzprintf(stream_, ")\n");
sta::print(stream_, ")\n");
}
void
@ -714,11 +708,11 @@ SdfWriter::writeWidthCheck(const Pin *pin,
float max_width)
{
std::string pin_name = sdfPortName(pin);
gzprintf(stream_, " (WIDTH (%s %s) ",
sdfEdge(hi_low->asTransition()),
pin_name.c_str());
sta::print(stream_, " (WIDTH ({} {}) ",
sdfEdge(hi_low->asTransition()),
pin_name);
writeSdfTriple(min_width, max_width);
gzprintf(stream_, ")\n");
sta::print(stream_, ")\n");
}
void
@ -726,9 +720,9 @@ SdfWriter::writePeriodCheck(const Pin *pin,
float min_period)
{
std::string pin_name = sdfPortName(pin);
gzprintf(stream_, " (PERIOD %s ", pin_name.c_str());
sta::print(stream_, " (PERIOD {} ", pin_name);
writeSdfTriple(min_period, min_period);
gzprintf(stream_, ")\n");
sta::print(stream_, ")\n");
}
const char *

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "Bfs.hh"
@ -37,10 +37,10 @@
namespace sta {
BfsIterator::BfsIterator(BfsIndex bfs_index,
Level level_min,
Level level_max,
SearchPred *search_pred,
StaState *sta) :
Level level_min,
Level level_max,
SearchPred *search_pred,
StaState *sta) :
StaState(sta),
bfs_index_(bfs_index),
level_min_(level_min),
@ -68,9 +68,7 @@ BfsIterator::ensureSize()
}
}
BfsIterator::~BfsIterator()
{
}
BfsIterator::~BfsIterator() {}
void
BfsIterator::clear()
@ -80,7 +78,7 @@ BfsIterator::clear()
VertexSeq &level_vertices = queue_[level];
for (Vertex *vertex : level_vertices) {
if (vertex)
vertex->setBfsInQueue(bfs_index_, false);
vertex->setBfsInQueue(bfs_index_, false);
}
level_vertices.clear();
incrLevel(level);
@ -91,18 +89,18 @@ BfsIterator::clear()
void
BfsIterator::reportEntries() const
{
for (Level level=first_level_; levelLessOrEqual(level, last_level_);incrLevel(level)){
for (Level level = first_level_; levelLessOrEqual(level, last_level_);
incrLevel(level)) {
const VertexSeq &level_vertices = queue_[level];
if (!level_vertices.empty()) {
report_->reportLine("Level %d", level);
report_->report("Level {}", level);
for (Vertex *vertex : level_vertices)
report_->reportLine(" %s",
vertex ? vertex->to_string(this).c_str() : "NULL");
report_->report(" {}", vertex ? vertex->to_string(this) : "NULL");
}
}
}
void
void
BfsIterator::deleteEntries(Level level)
{
VertexSeq &level_vertices = queue_[level];
@ -134,11 +132,11 @@ BfsIterator::enqueueAdjacentVertices(Vertex *vertex,
int
BfsIterator::visit(Level to_level,
VertexVisitor *visitor)
VertexVisitor *visitor)
{
int visit_count = 0;
while (levelLessOrEqual(first_level_, last_level_)
&& levelLessOrEqual(first_level_, to_level)) {
&& levelLessOrEqual(first_level_, to_level)) {
Level level = first_level_;
VertexSeq &level_vertices = queue_[level];
incrLevel(first_level_);
@ -162,7 +160,7 @@ BfsIterator::visit(Level to_level,
int
BfsIterator::visitParallel(Level to_level,
VertexVisitor *visitor)
VertexVisitor *visitor)
{
size_t thread_count = thread_count_;
int visit_count = 0;
@ -170,15 +168,15 @@ BfsIterator::visitParallel(Level to_level,
if (thread_count == 1)
visit_count = visit(to_level, visitor);
else {
std::vector<VertexVisitor*> visitors;
std::vector<VertexVisitor *> visitors;
for (int k = 0; k < thread_count_; k++)
visitors.push_back(visitor->copy());
visitors.push_back(visitor->copy());
while (levelLessOrEqual(first_level_, last_level_)
&& levelLessOrEqual(first_level_, to_level)) {
VertexSeq &level_vertices = queue_[first_level_];
&& levelLessOrEqual(first_level_, to_level)) {
VertexSeq &level_vertices = queue_[first_level_];
Level level = first_level_;
incrLevel(first_level_);
if (!level_vertices.empty()) {
incrLevel(first_level_);
if (!level_vertices.empty()) {
size_t vertex_count = level_vertices.size();
if (vertex_count < thread_count) {
for (Vertex *vertex : level_vertices) {
@ -196,7 +194,7 @@ BfsIterator::visitParallel(Level to_level,
for (size_t k = 0; k < thread_count; k++) {
// Last thread gets the left overs.
size_t to = (k == thread_count - 1) ? vertex_count : from + chunk_size;
dispatch_queue_->dispatch( [=, this](int) {
dispatch_queue_->dispatch([=, this](int) {
for (size_t i = from; i < to; i++) {
Vertex *vertex = level_vertices[i];
if (vertex) {
@ -210,13 +208,13 @@ BfsIterator::visitParallel(Level to_level,
}
dispatch_queue_->finishTasks();
}
visitor->levelFinished();
level_vertices.clear();
visitor->levelFinished();
level_vertices.clear();
visit_count += vertex_count;
}
}
}
for (VertexVisitor *visitor : visitors)
delete visitor;
delete visitor;
}
}
return visit_count;
@ -233,7 +231,7 @@ BfsIterator::hasNext(Level to_level)
{
findNext(to_level);
return levelLessOrEqual(first_level_, last_level_)
&& !queue_[first_level_].empty();
&& !queue_[first_level_].empty();
}
Vertex *
@ -250,16 +248,16 @@ void
BfsIterator::findNext(Level to_level)
{
while (levelLessOrEqual(first_level_, last_level_)
&& levelLessOrEqual(first_level_, to_level)) {
&& levelLessOrEqual(first_level_, to_level)) {
VertexSeq &level_vertices = queue_[first_level_];
// Skip null entries from deleted vertices.
while (!level_vertices.empty()) {
Vertex *vertex = level_vertices.back();
if (vertex == nullptr)
level_vertices.pop_back();
level_vertices.pop_back();
else {
checkLevel(vertex, first_level_);
return;
return;
}
}
incrLevel(first_level_);
@ -269,8 +267,7 @@ BfsIterator::findNext(Level to_level)
void
BfsIterator::enqueue(Vertex *vertex)
{
debugPrint(debug_, "bfs", 2, "enqueue %s",
vertex->to_string(this).c_str());
debugPrint(debug_, "bfs", 2, "enqueue {}", vertex->to_string(this));
if (!vertex->bfsInQueue(bfs_index_)) {
Level level = vertex->level();
LockGuard lock(queue_lock_);
@ -279,9 +276,9 @@ BfsIterator::enqueue(Vertex *vertex)
queue_[level].push_back(vertex);
if (levelLess(last_level_, level))
last_level_ = level;
last_level_ = level;
if (levelLess(level, first_level_))
first_level_ = level;
first_level_ = level;
}
}
}
@ -300,17 +297,15 @@ BfsIterator::checkInQueue(Vertex *vertex)
if (static_cast<Level>(queue_.size()) > level) {
for (Vertex *v : queue_[level]) {
if (v == vertex) {
if (vertex->bfsInQueue(bfs_index_))
return;
else
debugPrint(debug_, "bfs", 1, "extra %s",
vertex->to_string(this).c_str());
if (vertex->bfsInQueue(bfs_index_))
return;
else
debugPrint(debug_, "bfs", 1, "extra {}", vertex->to_string(this));
}
}
}
if (vertex->bfsInQueue(bfs_index_))
debugPrint(debug_, "brs", 1, "missing %s",
vertex->to_string(this).c_str());
debugPrint(debug_, "brs", 1, "missing {}", vertex->to_string(this));
}
void
@ -318,10 +313,8 @@ BfsIterator::checkLevel(Vertex *vertex,
Level level)
{
if (vertex->level() != level)
report_->error(2300, "vertex %s level %d != bfs level %d",
vertex->to_string(this).c_str(),
vertex->level(),
level);
report_->error(2300, "vertex {} level {} != bfs level {}",
vertex->to_string(this), vertex->level(), level);
}
void
@ -336,14 +329,12 @@ BfsIterator::remove(Vertex *vertex)
{
// If the iterator has not been inited the queue will be empty.
Level level = vertex->level();
if (vertex->bfsInQueue(bfs_index_)
&& static_cast<Level>(queue_.size()) > level) {
debugPrint(debug_, "bfs", 2, "remove %s",
vertex->to_string(this).c_str());
if (vertex->bfsInQueue(bfs_index_) && static_cast<Level>(queue_.size()) > level) {
debugPrint(debug_, "bfs", 2, "remove {}", vertex->to_string(this));
for (Vertex *&v : queue_[level]) {
if (v == vertex) {
v = nullptr;
vertex->setBfsInQueue(bfs_index_, false);
v = nullptr;
vertex->setBfsInQueue(bfs_index_, false);
break;
}
}
@ -353,9 +344,13 @@ BfsIterator::remove(Vertex *vertex)
////////////////////////////////////////////////////////////////
BfsFwdIterator::BfsFwdIterator(BfsIndex bfs_index,
SearchPred *search_pred,
StaState *sta) :
BfsIterator(bfs_index, 0, level_max, search_pred, sta)
SearchPred *search_pred,
StaState *sta) :
BfsIterator(bfs_index,
0,
level_max,
search_pred,
sta)
{
}
@ -374,14 +369,14 @@ BfsFwdIterator::incrLevel(Level &level) const
bool
BfsFwdIterator::levelLessOrEqual(Level level1,
Level level2) const
Level level2) const
{
return level1 <= level2;
}
bool
BfsFwdIterator::levelLess(Level level1,
Level level2) const
Level level2) const
{
return level1 < level2;
}
@ -395,9 +390,8 @@ BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex,
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *to_vertex = edge->to(graph_);
if (search_pred->searchThru(edge)
&& search_pred->searchTo(to_vertex))
enqueue(to_vertex);
if (search_pred->searchThru(edge) && search_pred->searchTo(to_vertex))
enqueue(to_vertex);
}
}
}
@ -422,9 +416,13 @@ BfsFwdIterator::enqueueAdjacentVertices(Vertex *vertex,
////////////////////////////////////////////////////////////////
BfsBkwdIterator::BfsBkwdIterator(BfsIndex bfs_index,
SearchPred *search_pred,
StaState *sta) :
BfsIterator(bfs_index, level_max, 0, search_pred, sta)
SearchPred *search_pred,
StaState *sta) :
BfsIterator(bfs_index,
level_max,
0,
search_pred,
sta)
{
}
@ -443,14 +441,14 @@ BfsBkwdIterator::incrLevel(Level &level) const
bool
BfsBkwdIterator::levelLessOrEqual(Level level1,
Level level2) const
Level level2) const
{
return level1 >= level2;
}
bool
BfsBkwdIterator::levelLess(Level level1,
Level level2) const
Level level2) const
{
return level1 > level2;
}
@ -464,9 +462,8 @@ BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex,
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *from_vertex = edge->from(graph_);
if (search_pred->searchFrom(from_vertex)
&& search_pred->searchThru(edge))
enqueue(from_vertex);
if (search_pred->searchFrom(from_vertex) && search_pred->searchThru(edge))
enqueue(from_vertex);
}
}
}
@ -488,4 +485,4 @@ BfsBkwdIterator::enqueueAdjacentVertices(Vertex *vertex,
}
}
} // namespace
} // namespace sta

View File

@ -140,8 +140,8 @@ CheckMinPulseWidths::checkVertex(Vertex *vertex,
Path *close_path = check.closePath(sta_);
// Don't bother visiting if nobody is home.
if (close_path) {
debugPrint(debug, "mpw", 2, "%s %s %s",
path_vertex->to_string(sta_).c_str(),
debugPrint(debug, "mpw", 2, "{} {} {}",
path_vertex->to_string(sta_),
path->transition(sta_) == RiseFall::rise() ? "(high)" : "(low)",
delayAsString(check.slack(sta_), sta_));
if (violators) {
@ -219,17 +219,17 @@ MinPulseWidthCheck::closePath(const StaState *sta) const
open_tag->isSegmentStart(),
open_tag->states(),
false);
debugPrint(sta->debug(), "mpw", 3, " open %s",
open_tag->to_string(sta).c_str());
debugPrint(sta->debug(), "mpw", 3, " close %s",
close_tag.to_string(sta).c_str());
debugPrint(sta->debug(), "mpw", 3, " open {}",
open_tag->to_string(sta));
debugPrint(sta->debug(), "mpw", 3, " close {}",
close_tag.to_string(sta));
VertexPathIterator close_iter(open_path_->vertex(sta), scene, close_min_max,
close_rf, sta);
while (close_iter.hasNext()) {
Path *close_path = close_iter.next();
if (Tag::matchNoPathAp(close_path->tag(sta), &close_tag)) {
debugPrint(sta->debug(), "mpw", 3, " match %s",
close_path->tag(sta)->to_string(sta).c_str());
debugPrint(sta->debug(), "mpw", 3, " match {}",
close_path->tag(sta)->to_string(sta));
return close_path;
}
}

View File

@ -123,8 +123,7 @@ CheckTiming::checkNoInputDelay()
}
}
delete pin_iter;
pushPinErrors("Warning: There %is %d input port%s missing set_input_delay.",
no_arrival);
pushPinErrors("Warning: There {} {} input port{} missing set_input_delay.",no_arrival);
}
void
@ -132,7 +131,7 @@ CheckTiming::checkNoOutputDelay()
{
PinSet no_departure(network_);
checkNoOutputDelay(no_departure);
pushPinErrors("Warning: There %is %d output port%s missing set_output_delay.",
pushPinErrors("Warning: There {} {} output port{} missing set_output_delay.",
no_departure);
}
@ -179,12 +178,24 @@ CheckTiming::checkRegClks(bool reg_multiple_clks,
if (reg_multiple_clks && clks && clks->size() > 1)
multiple_clk_pins.insert(pin);
}
pushPinErrors("Warning: There %is %d unclocked register/latch pin%s.",
pushPinErrors("Warning: There {} {} unclocked register/latch pin{}.",
no_clk_pins);
pushPinErrors("Warning: There %is %d register/latch pin%s with multiple clocks.",
pushPinErrors("Warning: There {} {} register/latch pin{} with multiple clocks.",
multiple_clk_pins);
}
static const char *
plurality(int n)
{
return n == 1 ? "is" : "are";
}
static const char *
pluralSuffix(int n)
{
return n == 1 ? "" : "s";
}
void
CheckTiming::checkLoops()
{
@ -198,11 +209,11 @@ CheckTiming::checkLoops()
loop_count++;
}
if (loop_count > 0) {
std::string error_msg;
errorMsgSubst("Warning: There %is %d combinational loop%s in the design.",
loop_count, error_msg);
CheckError *error = new CheckError;
error->push_back(error_msg);
error->push_back(sta::format("Warning: There {} {} combinational loop{} in the design.",
plurality(loop_count),
loop_count,
pluralSuffix(loop_count)));
for (GraphLoop *loop : loops) {
if (loop->isCombinational()) {
@ -232,7 +243,7 @@ CheckTiming::checkUnconstrainedEndpoints()
PinSet unconstrained_ends(network_);
checkUnconstrainedOutputs(unconstrained_ends);
checkUnconstrainedSetups(unconstrained_ends);
pushPinErrors("Warning: There %is %d unconstrained endpoint%s.",
pushPinErrors("Warning: There {} {} unconstrained endpoint{}.",
unconstrained_ends);
}
@ -338,27 +349,21 @@ CheckTiming::checkGeneratedClocks()
gen_clk_errors.insert(clk);
}
}
pushClkErrors("Warning: There %is %d generated clock%s that %is not connected to a clock source.",
pushClkErrors("Warning: There {} {} generated clock{} not connected to a clock source.",
gen_clk_errors);
}
// Report the "msg" error for each pin in "pins".
//
// Substitutions in msg are done as follows if the pin count is one
// or greater than one.
// %is - is/are
// %d - pin count
// %s - s/""
// %a - a/""
void
CheckTiming::pushPinErrors(const char *msg,
CheckTiming::pushPinErrors(std::string_view msg,
PinSet &pins)
{
if (!pins.empty()) {
CheckError *error = new CheckError;
std::string error_msg;
errorMsgSubst(msg, pins.size(), error_msg);
error->push_back(error_msg);
error->push_back(sta::formatRuntime(msg,
plurality(pins.size()),
pins.size(),
pluralSuffix(pins.size())));
// Sort the error pins so the output is independent of the order
// the the errors are discovered.
PinSeq pins1 = sortByPathName(&pins, network_);
@ -375,9 +380,10 @@ CheckTiming::pushClkErrors(const char *msg,
{
if (!clks.empty()) {
CheckError *error = new CheckError;
std::string error_msg;
errorMsgSubst(msg, clks.size(), error_msg);
error->push_back(error_msg);
error->push_back(sta::formatRuntime(msg,
plurality(clks.size()),
clks.size(),
pluralSuffix(clks.size())));
// Sort the error clks so the output is independent of the order
// the the errors are discovered.
ClockSeq clks1 = sortByName(&clks);
@ -388,47 +394,4 @@ CheckTiming::pushClkErrors(const char *msg,
}
}
// Copy msg making substitutions for singular/plurals.
void
CheckTiming::errorMsgSubst(const char *msg,
int obj_count,
std::string &error_msg)
{
for (const char *s = msg; *s; s++) {
char ch = *s;
if (ch == '%') {
char flag = s[1];
if (flag == 'i') {
if (obj_count > 1)
error_msg += "are";
else
error_msg += "is";
s += 2;
}
else if (flag == 'a') {
if (obj_count == 1) {
error_msg += 'a';
s++;
}
else
// Skip space after %a.
s += 2;
}
else if (flag == 's') {
if (obj_count > 1)
error_msg += 's';
s++;
}
else if (flag == 'd') {
error_msg += std::to_string(obj_count);
s++;
}
else
criticalError(245, "unknown print flag");
}
else
error_msg += ch;
}
}
} // namespace

View File

@ -70,13 +70,10 @@ protected:
bool hasClkedCheck(Vertex *vertex);
bool hasMaxDelay(Pin *pin);
void checkGeneratedClocks();
void pushPinErrors(const char *msg,
void pushPinErrors(std::string_view msg,
PinSet &pins);
void pushClkErrors(const char *msg,
ClockSet &clks);
void errorMsgSubst(const char *msg,
int count,
std::string &error_msg);
CheckErrorSeq errors_;
const Mode *mode_;

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "ClkLatency.hh"
@ -55,8 +55,7 @@ ClkLatency::findClkDelays(const Clock *clk,
clks.push_back(clk);
SceneSet scenes;
scenes.insert(scene);
ClkDelayMap clk_delay_map = findClkDelays(clks, scenes,
include_internal_latency);
ClkDelayMap clk_delay_map = findClkDelays(clks, scenes, include_internal_latency);
return clk_delay_map[clk];
}
@ -88,7 +87,7 @@ ClkLatency::reportClkLatency(const Clock *clk,
int digits)
{
Unit *time_unit = units_->timeUnit();
report_->reportLine("Clock %s", clk->name());
report_->report("Clock {}", clk->name());
for (const RiseFall *src_rf : RiseFall::range()) {
for (const RiseFall *end_rf : RiseFall::range()) {
Path path_min;
@ -97,47 +96,41 @@ ClkLatency::reportClkLatency(const Clock *clk,
float internal_latency_min;
Delay latency_min;
bool exists_min;
clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min,
delay_min, internal_latency_min, latency_min,
path_min, exists_min);
clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min, delay_min,
internal_latency_min, latency_min, path_min, exists_min);
Path path_max;
Delay insertion_max;
Delay delay_max;
float internal_latency_max;
Delay latency_max;
bool exists_max;
clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max,
delay_max, internal_latency_max, latency_max,
path_max, exists_max);
clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max, delay_max,
internal_latency_max, latency_max, path_max, exists_max);
if (exists_min & exists_max) {
report_->reportLine("%s -> %s",
src_rf->name(),
end_rf->name());
report_->reportLine(" min max");
report_->reportLine("%7s %7s source latency",
delayAsString(insertion_min, MinMax::min(), digits, this),
delayAsString(insertion_max, MinMax::max(), digits, this));
report_->reportLine("%7s %7s network latency %s",
delayAsString(delay_min, MinMax::min(), digits, this),
"",
sdc_network_->pathName(path_min.pin(this)));
report_->reportLine("%7s %7s network latency %s",
"",
delayAsString(delay_max, MinMax::max(), digits, this),
sdc_network_->pathName(path_max.pin(this)));
if (internal_latency_min != 0.0
|| internal_latency_max != 0.0)
report_->reportLine("%7s %7s internal clock latency",
time_unit->asString(internal_latency_min, digits),
time_unit->asString(internal_latency_max, digits));
report_->reportLine("---------------");
report_->reportLine("%7s %7s latency",
delayAsString(latency_min, MinMax::min(), digits, this),
delayAsString(latency_max, MinMax::max(), digits, this));
report_->report("{} -> {}", src_rf->name(), end_rf->name());
report_->report(" min max");
report_->report("{:>7} {:>7} source latency",
delayAsString(insertion_min, MinMax::min(), digits, this),
delayAsString(insertion_max, MinMax::max(), digits, this));
report_->report("{:>7} {:>7} network latency {}",
delayAsString(delay_min, MinMax::min(), digits, this),
"",
sdc_network_->pathName(path_min.pin(this)));
report_->report("{:>7} {:>7} network latency {}",
"",
delayAsString(delay_max, MinMax::max(), digits, this),
sdc_network_->pathName(path_max.pin(this)));
if (internal_latency_min != 0.0 || internal_latency_max != 0.0)
report_->report("{:>7} {:>7} internal clock latency",
time_unit->asString(internal_latency_min, digits),
time_unit->asString(internal_latency_max, digits));
report_->report("---------------");
report_->report("{:>7} {:>7} latency",
delayAsString(latency_min, MinMax::min(), digits, this),
delayAsString(latency_max, MinMax::max(), digits, this));
Delay skew = delayDiff(latency_max, latency_min, this);
report_->reportLine(" %7s skew",
delayAsString(skew, MinMax::max(), digits, this));
report_->report(" {:>7} skew",
delayAsString(skew, MinMax::max(), digits, this));
report_->reportBlankLine();
}
}
@ -164,9 +157,7 @@ ClkLatency::findClkDelays(ConstClockSeq &clks,
Path *path = path_iter.next();
const Scene *path_scene = path->scene(this);
const Clock *path_clk = path->clock(this);
if (path_clk
&& scenes.contains(path_scene)
&& clk_set.contains(path_clk)) {
if (path_clk && scenes.contains(path_scene) && clk_set.contains(path_clk)) {
auto delays_itr = clk_delay_map.find(path_clk);
if (delays_itr != clk_delay_map.end()) {
const ClockEdge *path_clk_edge = path->clkEdge(this);
@ -278,7 +269,6 @@ Delay
ClkDelays::latency(Path *clk_path,
StaState *sta)
{
Delay insertion = insertionDelay(clk_path, sta);
Delay delay1 = delay(clk_path, sta);
float lib_clk_delay = clkTreeDelay(clk_path, sta);
@ -321,4 +311,4 @@ ClkDelays::clkTreeDelay(Path *clk_path,
return port->clkTreeDelay(slew, rf, min_max);
}
} // namespace
} // namespace sta

View File

@ -1,30 +1,30 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "ClkSkew.hh"
#include <cmath> // abs
#include <cmath> // abs
#include <algorithm>
#include <vector>
#include <unordered_set>
@ -78,12 +78,12 @@ ClkSkews::reportClkSkew(ConstClockSeq &clks,
sort(sorted_clks, ClkNameLess());
for (const Clock *clk : sorted_clks) {
report_->reportLine("Clock %s", clk->name());
report_->report("Clock {}", clk->name());
auto skew_itr = skews_.find(clk);
if (skew_itr != skews_.end())
reportClkSkew(skew_itr->second[setup_hold->index()], digits);
else
report_->reportLine("No launch/capture paths found.");
report_->report("No launch/capture paths found.");
report_->reportBlankLine();
}
}
@ -104,33 +104,33 @@ ClkSkews::reportClkSkew(ClkSkew &clk_skew,
if (src_internal_clk_latency != 0.0)
delayDecr(src_latency, src_internal_clk_latency, this);
report_->reportLine("%7s source latency %s %s",
delayAsString(src_latency, src_min_max, digits, this),
sdc_network_->pathName(src_path->pin(this)),
src_path->transition(this)->shortName());
report_->report("{:>7} source latency {} {}",
delayAsString(src_latency, src_min_max, digits, this),
sdc_network_->pathName(src_path->pin(this)),
src_path->transition(this)->shortName());
if (src_internal_clk_latency != 0.0)
report_->reportLine("%7s source internal clock delay",
time_unit->asString(src_internal_clk_latency, digits));
report_->report("{:>7} source internal clock delay",
time_unit->asString(src_internal_clk_latency, digits));
if (tgt_internal_clk_latency != 0.0)
tgt_latency -= tgt_internal_clk_latency;
report_->reportLine("%7s target latency %s %s",
time_unit->asString(-tgt_latency, digits),
sdc_network_->pathName(tgt_path->pin(this)),
tgt_path->transition(this)->shortName());
report_->report("{:>7} target latency {} {}",
time_unit->asString(-tgt_latency, digits),
sdc_network_->pathName(tgt_path->pin(this)),
tgt_path->transition(this)->shortName());
if (tgt_internal_clk_latency != 0.0)
report_->reportLine("%7s target internal clock delay",
time_unit->asString(-tgt_internal_clk_latency, digits));
report_->report("{:>7} target internal clock delay",
time_unit->asString(-tgt_internal_clk_latency, digits));
if (uncertainty != 0.0)
report_->reportLine("%7s clock uncertainty",
time_unit->asString(uncertainty, digits));
report_->reportLine("%7s CRPR",
delayAsString(delayDiff(0.0, clk_skew.crpr(this), this),
MinMax::max(), digits, this));
report_->reportLine("--------------");
report_->reportLine("%7s %s skew",
delayAsString(clk_skew.skew(), MinMax::max(), digits, this),
src_path->minMax(this) == MinMax::max() ? "setup" : "hold");
report_->report("{:>7} clock uncertainty",
time_unit->asString(uncertainty, digits));
report_->report("{:>7} CRPR",
delayAsString(delayDiff(0.0, clk_skew.crpr(this), this),
MinMax::max(), digits, this));
report_->report("--------------");
report_->report("{:>7} {} skew",
delayAsString(clk_skew.skew(), MinMax::max(), digits, this),
src_path->minMax(this) == MinMax::max() ? "setup" : "hold");
}
static float
@ -174,11 +174,9 @@ void
ClkSkews::findClkSkew(ConstClockSeq &clks,
const SceneSeq &scenes,
bool include_internal_latency)
{
if (scenes == scenes_
&& include_internal_latency == include_internal_latency_
&& clks == clks_
&& !skews_.empty())
{
if (scenes == scenes_ && include_internal_latency == include_internal_latency_
&& clks == clks_ && !skews_.empty())
return;
skews_.clear();
@ -206,14 +204,14 @@ ClkSkews::findClkSkew(ConstClockSeq &clks,
// Reduce skews from each register source.
for (size_t i = 0; i < partial_skews.size(); i++) {
for (auto& [clk, partial_skew] : partial_skews[i]) {
for (auto &[clk, partial_skew] : partial_skews[i]) {
auto itr = skews_.find(clk);
if (itr == skews_.end()) {
// Insert new entry using emplace with piecewise_construct
// This will default-construct the array, then we copy the elements
auto result = skews_.emplace(std::piecewise_construct,
std::forward_as_tuple(clk),
std::make_tuple());
auto result =
skews_.emplace(std::piecewise_construct, std::forward_as_tuple(clk),
std::make_tuple());
itr = result.first;
// Copy array elements
for (int setup_hold_idx : SetupHold::rangeIndex())
@ -231,7 +229,8 @@ ClkSkews::findClkSkew(ConstClockSeq &clks,
if (partial_skew_max > final_skew_max
|| (fuzzyEqual(partial_skew_max, final_skew_max)
// Break ties based on source/target path names.
&& ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew, this)))
&& ClkSkew::srcTgtPathNameLess(partial_skew_val, final_skew,
this)))
final_skew = partial_skew_val;
}
}
@ -269,9 +268,8 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex,
if (edge->role()->genericRole() == TimingRole::regClkToQ()) {
Vertex *q_vertex = edge->to(graph_);
const RiseFall *rf = edge->timingArcSet()->isRisingFallingEdge();
const RiseFallBoth *src_rf = rf
? rf->asRiseFallBoth()
: RiseFallBoth::riseFall();
const RiseFallBoth *src_rf =
rf ? rf->asRiseFallBoth() : RiseFallBoth::riseFall();
findClkSkewFrom(src_vertex, q_vertex, src_rf, skews);
}
}
@ -291,12 +289,11 @@ ClkSkews::findClkSkewFrom(Vertex *src_vertex,
const TimingRole *role = edge->role();
if (role->genericRole() == TimingRole::setup()
|| role->genericRole() == TimingRole::hold()) {
Vertex *tgt_vertex = edge->from(graph_);
const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge();
const RiseFallBoth *tgt_rf = tgt_rf1
? tgt_rf1->asRiseFallBoth()
: RiseFallBoth::riseFall();
findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf, skews);
Vertex *tgt_vertex = edge->from(graph_);
const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge();
const RiseFallBoth *tgt_rf =
tgt_rf1 ? tgt_rf1->asRiseFallBoth() : RiseFallBoth::riseFall();
findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf, skews);
}
}
}
@ -325,8 +322,7 @@ ClkSkews::findClkSkew(Vertex *src_vertex,
while (tgt_iter.hasNext()) {
Path *tgt_path = tgt_iter.next();
const Clock *tgt_clk = tgt_path->clock(this);
if (tgt_clk == src_clk
&& tgt_path->isClock(this)
if (tgt_clk == src_clk && tgt_path->isClock(this)
&& tgt_rf->matches(tgt_path->transition(this))
&& tgt_path->minMax(this) == tgt_min_max
&& tgt_path->scene(this) == src_scene) {
@ -334,7 +330,7 @@ ClkSkews::findClkSkew(Vertex *src_vertex,
const SetupHold *setup_hold = src_path->minMax(this);
ClkSkew &clk_skew = skews[src_clk][setup_hold->index()];
debugPrint(debug_, "clk_skew", 2,
"%s %s %s -> %s %s %s crpr = %s skew = %s",
"{} {} {} -> {} {} {} crpr = {} skew = {}",
network_->pathName(src_path->pin(this)),
src_path->transition(this)->shortName(),
delayAsString(probe.srcLatency(this), src_min_max, this),
@ -356,14 +352,14 @@ VertexSet
ClkSkews::findFanout(Vertex *from)
{
VertexSet endpoints = makeVertexSet(this);
std::unordered_set<Vertex*> visited;
std::unordered_set<Vertex *> visited;
findFanout1(from, visited, endpoints);
return endpoints;
}
void
ClkSkews::findFanout1(Vertex *from,
std::unordered_set<Vertex*> &visited,
std::unordered_set<Vertex *> &visited,
VertexSet &endpoints)
{
visited.insert(from);
@ -439,7 +435,8 @@ float
ClkSkew::tgtLatency(const StaState *sta)
{
Arrival tgt_arrival = tgt_path_->arrival();
return delayAsFloat(delaySum(delayDiff(tgt_arrival, tgt_path_->clkEdge(sta)->time(),sta),
return delayAsFloat(delaySum(delayDiff(tgt_arrival,
tgt_path_->clkEdge(sta)->time(),sta),
clkTreeDelay(tgt_path_, sta), sta));
}
@ -477,8 +474,8 @@ float
ClkSkew::uncertainty(const StaState *sta)
{
const TimingRole *check_role = (src_path_->minMax(sta) == SetupHold::max())
? TimingRole::setup()
: TimingRole::hold();
? TimingRole::setup()
: TimingRole::hold();
// Uncertainty decreases slack, but increases skew.
return -PathEnd::checkTgtClkUncertainty(tgt_path_, tgt_path_->clkEdge(sta),
check_role, sta);
@ -495,8 +492,7 @@ ClkSkew::srcTgtPathNameLess(ClkSkew &clk_skew1,
const char *tgt_path1 = network->pathName(clk_skew1.tgtPath()->pin(sta));
const char *tgt_path2 = network->pathName(clk_skew2.tgtPath()->pin(sta));
return stringLess(src_path1, src_path2)
|| (stringEqual(src_path1, src_path2)
&& stringEqual(tgt_path1, tgt_path2));
|| (stringEqual(src_path1, src_path2) && stringEqual(tgt_path1, tgt_path2));
}
////////////////////////////////////////////////////////////////
@ -512,10 +508,9 @@ FanOutSrchPred::searchThru(Edge *edge,
{
const TimingRole *role = edge->role();
return SearchPred1::searchThru(edge, mode)
&& (role == TimingRole::wire()
|| role == TimingRole::combinational()
|| role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable());
&& (role == TimingRole::wire() || role == TimingRole::combinational()
|| role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable());
}
} // namespace
} // namespace sta

View File

@ -236,7 +236,7 @@ CheckCrpr::findCrpr(const Path *src_clk_path,
&& tgt_clk_path2 && !tgt_clk_path2->isNull()
&& (src_clk_path2->transition(this) == tgt_clk_path2->transition(this)
|| same_pin)) {
debugPrint(debug_, "crpr", 2, "crpr pin %s",
debugPrint(debug_, "crpr", 2, "crpr pin {}",
network_->pathName(src_clk_path2->pin(this)));
crpr = findCrpr1(src_clk_path2, tgt_clk_path2);
crpr_pin = src_clk_path2->pin(this);
@ -289,12 +289,12 @@ CheckCrpr::findCrpr1(const Path *src_clk_path,
// is the min of the source and target max-min delay.
float src_delta = crprArrivalDiff(src_clk_path);
float tgt_delta = crprArrivalDiff(tgt_clk_path);
debugPrint(debug_, "crpr", 2, " src delta %s",
debugPrint(debug_, "crpr", 2, " src delta {}",
delayAsString(src_delta, this));
debugPrint(debug_, "crpr", 2, " tgt delta %s",
debugPrint(debug_, "crpr", 2, " tgt delta {}",
delayAsString(tgt_delta, this));
float common_delay = std::min(src_delta, tgt_delta);
debugPrint(debug_, "crpr", 2, " %s delta %s",
debugPrint(debug_, "crpr", 2, " {} delta {}",
network_->pathName(src_clk_path->pin(this)),
delayAsString(common_delay, this));
return common_delay;

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "Genclks.hh"
@ -85,10 +85,7 @@ GenclkInfo::GenclkInfo(Clock *gclk,
{
}
GenclkInfo::~GenclkInfo()
{
delete src_filter_;
}
GenclkInfo::~GenclkInfo() { delete src_filter_; }
void
GenclkInfo::setFoundLatchFdbkEdges(bool found)
@ -202,7 +199,7 @@ Genclks::ensureInsertionDelays()
// Generated clocks derived from a generated clock inherit its
// insertion delay, so sort the clocks by source pin level.
sort(gclks , ClockPinMaxLevelLess(this));
sort(gclks, ClockPinMaxLevelLess(this));
for (Clock *gclk : gclks) {
if (gclk->masterClk()) {
@ -232,8 +229,7 @@ GenClkMasterSearchPred::GenClkMasterSearchPred(const StaState *sta) :
bool
GenClkMasterSearchPred::searchThruAllow(const TimingRole *role) const
{
return (role->isWire()
|| role == TimingRole::combinational()
return (role->isWire() || role == TimingRole::combinational()
|| role->regClkToQ());
}
@ -245,7 +241,7 @@ Genclks::checkMaster(Clock *gclk,
{
ensureMaster(gclk, sdc);
if (gclk->masterClk() == nullptr)
report_->warn(1060, "no master clock found for generated clock %s.",
report_->warn(1060, "no master clock found for generated clock {}.",
gclk->name());
}
@ -266,8 +262,7 @@ Genclks::ensureMaster(Clock *gclk,
// Master source pin can actually be a clock source pin.
if (master_clk != gclk) {
gclk->setInferedMasterClk(master_clk);
debugPrint(debug_, "genclk", 2, " %s master clk %s",
gclk->name(),
debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(),
master_clk->name());
found_master = true;
master_clk_count++;
@ -291,8 +286,7 @@ Genclks::ensureMaster(Clock *gclk,
// Master source pin can actually be a clock source pin.
if (master_clk != gclk) {
gclk->setInferedMasterClk(master_clk);
debugPrint(debug_, "genclk", 2, " %s master clk %s",
gclk->name(),
debugPrint(debug_, "genclk", 2, " {} master clk {}", gclk->name(),
master_clk->name());
master_clk_count++;
break;
@ -305,9 +299,8 @@ Genclks::ensureMaster(Clock *gclk,
}
if (master_clk_count > 1)
report_->warn(1061,
"generated clock %s pin %s is in the fanout of multiple clocks.",
gclk->name(),
network_->pathName(src_pin));
"generated clock {} pin {} is in the fanout of multiple clocks.",
gclk->name(), network_->pathName(src_pin));
}
}
@ -346,8 +339,7 @@ GenClkFaninSrchPred::GenClkFaninSrchPred(Clock *gclk,
bool
GenClkFaninSrchPred::searchThruAllow(const TimingRole *role) const
{
return (role == TimingRole::combinational()
|| role == TimingRole::wire()
return (role == TimingRole::combinational() || role == TimingRole::wire()
|| !combinational_);
}
@ -363,8 +355,8 @@ Genclks::findFanin(Clock *gclk,
Vertex *vertex = iter.next();
if (!fanins.contains(vertex)) {
fanins.insert(vertex);
debugPrint(debug_, "genclk", 2, "gen clk %s fanin %s",
gclk->name(), vertex->to_string(this).c_str());
debugPrint(debug_, "genclk", 2, "gen clk {} fanin {}", gclk->name(),
vertex->to_string(this));
iter.enqueueAdjacentVertices(vertex, mode_);
}
}
@ -398,7 +390,7 @@ public:
bool searchThru(Edge *edge,
const Mode *mode) const override;
bool searchTo(const Vertex *to_vertex,
const Mode *mode) const override;
const Mode *mode) const override;
private:
bool isNonGeneratedClkPin(const Pin *pin,
@ -423,12 +415,11 @@ GenClkInsertionSearchPred::searchThru(Edge *edge,
{
const TimingRole *role = edge->role();
EdgeSet &fdbk_edges = genclk_info_->fdbkEdges();
return SearchPred0::searchThru(edge, mode)
&& !role->isTimingCheck()
&& (sta_->variables()->clkThruTristateEnabled()
|| !(role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable()))
&& !fdbk_edges.contains(edge);
return SearchPred0::searchThru(edge, mode) && !role->isTimingCheck()
&& (sta_->variables()->clkThruTristateEnabled()
|| !(role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable()))
&& !fdbk_edges.contains(edge);
}
bool
@ -437,11 +428,11 @@ GenClkInsertionSearchPred::searchTo(const Vertex *to_vertex,
{
Pin *to_pin = to_vertex->pin();
return SearchPred0::searchTo(to_vertex, mode)
// Propagate through other generated clock roots but not regular
// clock roots.
&& !(!gclk_->leafPins().contains(to_pin)
&& isNonGeneratedClkPin(to_pin, mode->sdc()))
&& genclk_info_->fanins().contains(const_cast<Vertex*>(to_vertex));
// Propagate through other generated clock roots but not regular
// clock roots.
&& !(!gclk_->leafPins().contains(to_pin)
&& isNonGeneratedClkPin(to_pin, mode->sdc()))
&& genclk_info_->fanins().contains(const_cast<Vertex *>(to_vertex));
}
bool
@ -463,8 +454,7 @@ GenClkInsertionSearchPred::isNonGeneratedClkPin(const Pin *pin,
void
Genclks::findInsertionDelays(Clock *gclk)
{
debugPrint(debug_, "genclk", 2, "find gen clk %s insertion",
gclk->name());
debugPrint(debug_, "genclk", 2, "find gen clk {} insertion", gclk->name());
GenclkInfo *genclk_info = makeGenclkInfo(gclk);
FilterPath *src_filter = genclk_info->srcFilter();
GenClkInsertionSearchPred srch_pred(gclk, genclk_info, this);
@ -492,7 +482,7 @@ Genclks::makeGenclkInfo(Clock *gclk)
GenclkInfo *
Genclks::genclkInfo(const Clock *gclk) const
{
return findKey(genclk_info_map_, const_cast<Clock*>(gclk));
return findKey(genclk_info_map_, const_cast<Clock *>(gclk));
}
FilterPath *
@ -516,8 +506,7 @@ void
Genclks::findLatchFdbkEdges(const Clock *clk)
{
GenclkInfo *genclk_info = genclkInfo(clk);
if (genclk_info
&& !genclk_info->foundLatchFdbkEdges())
if (genclk_info && !genclk_info->foundLatchFdbkEdges())
findLatchFdbkEdges(clk, genclk_info);
}
@ -563,15 +552,15 @@ Genclks::findLatchFdbkEdges(Vertex *from_vertex,
Edge *edge = edge_iter.next();
Vertex *to_vertex = edge->to(graph_);
if (path_vertices.contains(to_vertex)) {
debugPrint(debug_, "genclk", 2, " found feedback edge %s",
edge->to_string(this).c_str());
debugPrint(debug_, "genclk", 2, " found feedback edge {}",
edge->to_string(this));
fdbk_edges.insert(edge);
}
else if (srch_pred.searchThru(edge, mode_)
&& srch_pred.searchTo(to_vertex, mode_)
&& to_vertex->level() <= gclk_level)
findLatchFdbkEdges(to_vertex, gclk_level, srch_pred,
path_vertices, visited_vertices, fdbk_edges);
findLatchFdbkEdges(to_vertex, gclk_level, srch_pred, path_vertices,
visited_vertices, fdbk_edges);
}
path_vertices.erase(from_vertex);
}
@ -584,11 +573,11 @@ Genclks::makeSrcFilter(Clock *gclk,
ClockSet *from_clks = new ClockSet;
from_clks->insert(gclk->masterClk());
const RiseFallBoth *rf = RiseFallBoth::riseFall();
ExceptionFrom *from = sdc->makeExceptionFrom(nullptr,from_clks,nullptr,rf);
ExceptionFrom *from = sdc->makeExceptionFrom(nullptr, from_clks, nullptr, rf);
PinSet *thru_pins = new PinSet(network_);
thru_pins->insert(gclk->srcPin());
ExceptionThru *thru = sdc->makeExceptionThru(thru_pins,nullptr,nullptr,rf);
ExceptionThru *thru = sdc->makeExceptionThru(thru_pins, nullptr, nullptr, rf);
ExceptionThruSeq *thrus = new ExceptionThruSeq;
thrus->push_back(thru);
@ -608,7 +597,7 @@ Genclks::seedSrcPins(Clock *gclk,
for (const Pin *master_pin : master_clk->leafPins()) {
Vertex *vertex = graph_->pinDrvrVertex(master_pin);
if (vertex) {
debugPrint(debug_, "genclk", 2, " seed src pin %s",
debugPrint(debug_, "genclk", 2, " seed src pin {}",
network_->pathName(master_pin));
TagGroupBldr tag_bldr(true, this);
tag_bldr.init(vertex);
@ -619,8 +608,8 @@ Genclks::seedSrcPins(Clock *gclk,
for (const RiseFall *rf : RiseFall::range()) {
Arrival insert = search_->clockInsertion(master_clk, master_pin, rf,
min_max, early_late, mode_);
Tag *tag = makeTag(gclk, master_clk, master_pin, rf,
src_filter, insert, scene, min_max);
Tag *tag = makeTag(gclk, master_clk, master_pin, rf, src_filter, insert,
scene, min_max);
tag_bldr.setArrival(tag, insert);
}
}
@ -648,13 +637,11 @@ Genclks::makeTag(const Clock *gclk,
state = state->nextState();
ExceptionStateSet *states = new ExceptionStateSet();
states->insert(state);
const ClkInfo *clk_info = search_->findClkInfo(scene,
master_clk->edge(master_rf),
master_pin, true, nullptr, true,
nullptr, insert, 0.0, nullptr,
min_max, nullptr);
return search_->findTag(scene, master_rf, min_max, clk_info,
false, nullptr, false, states, true, nullptr);
const ClkInfo *clk_info = search_->findClkInfo(
scene, master_clk->edge(master_rf), master_pin, true, nullptr, true, nullptr,
insert, 0.0, nullptr, min_max, nullptr);
return search_->findTag(scene, master_rf, min_max, clk_info, false, nullptr, false,
states, true, nullptr);
}
class GenClkArrivalSearchPred : public EvalPred
@ -684,12 +671,10 @@ GenClkArrivalSearchPred::searchThru(Edge *edge,
{
const TimingRole *role = edge->role();
return EvalPred::searchThru(edge, mode)
&& (role == TimingRole::combinational()
|| role->isWire()
|| !combinational_)
&& (sta_->variables()->clkThruTristateEnabled()
|| !(role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable()));
&& (role == TimingRole::combinational() || role->isWire() || !combinational_)
&& (sta_->variables()->clkThruTristateEnabled()
|| !(role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable()));
}
// Override EvalPred::searchTo to search to generated clock pin.
@ -730,12 +715,14 @@ protected:
GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk,
BfsFwdIterator *insert_iter,
GenclkInfo *genclk_info,
const Mode *mode):
const Mode *mode) :
ArrivalVisitor(mode),
gclk_(gclk),
insert_iter_(insert_iter),
genclk_info_(genclk_info),
srch_pred_(gclk_, genclk_info, mode),
srch_pred_(gclk_,
genclk_info,
mode),
mode_(mode),
sdc_(mode->sdc()),
genclks_(mode->genclks())
@ -749,11 +736,15 @@ GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk,
bool always_to_endpoints,
SearchPred *pred,
const Mode *mode) :
ArrivalVisitor(always_to_endpoints, pred, mode),
ArrivalVisitor(always_to_endpoints,
pred,
mode),
gclk_(gclk),
insert_iter_(insert_iter),
genclk_info_(genclk_info),
srch_pred_(gclk, genclk_info, mode),
srch_pred_(gclk,
genclk_info,
mode),
mode_(mode),
sdc_(mode->sdc()),
genclks_(mode->genclks())
@ -770,8 +761,8 @@ GenclkSrcArrivalVisitor::copy() const
void
GenclkSrcArrivalVisitor::visit(Vertex *vertex)
{
debugPrint(debug_, "genclk", 2, "find gen clk insert arrival %s",
vertex->to_string(this).c_str());
debugPrint(debug_, "genclk", 2, "find gen clk insert arrival {}",
vertex->to_string(this));
tag_bldr_->init(vertex);
has_fanin_one_ = graph_->hasFaninOne(vertex);
genclks_->copyGenClkSrcPaths(vertex, tag_bldr_);
@ -787,8 +778,7 @@ Genclks::findSrcArrivals(Clock *gclk,
GenclkInfo *genclk_info)
{
GenClkArrivalSearchPred eval_pred(gclk, this);
GenclkSrcArrivalVisitor arrival_visitor(gclk, &insert_iter,
genclk_info, mode_);
GenclkSrcArrivalVisitor arrival_visitor(gclk, &insert_iter, genclk_info, mode_);
arrival_visitor.init(true, false, &eval_pred);
// This cannot restrict the search level because loops in the clock tree
// can circle back to the generated clock src pin.
@ -803,7 +793,7 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex,
{
auto itr = vertex_src_paths_map_.find(vertex);
if (itr != vertex_src_paths_map_.end()) {
const std::vector<const Path*> &src_paths = itr->second;
const std::vector<const Path *> &src_paths = itr->second;
for (const Path *path : src_paths) {
Path src_path = *path;
Path *prev_path = src_path.prevPath();
@ -811,11 +801,11 @@ Genclks::copyGenClkSrcPaths(Vertex *vertex,
Path *prev_vpath = Path::vertexPath(prev_path, this);
src_path.setPrevPath(prev_vpath);
}
debugPrint(debug_, "genclk", 3, "vertex %s insert genclk %s src path %s %ss",
src_path.vertex(this)->to_string(this).c_str(),
debugPrint(debug_, "genclk", 3, "vertex {} insert genclk {} src path {} {}s",
src_path.vertex(this)->to_string(this),
src_path.tag(this)->genClkSrcPathClk()->name(),
src_path.tag(this)->minMax()->to_string().c_str(),
src_path.tag(this)->to_string(true, false, this).c_str());
src_path.tag(this)->minMax()->to_string(),
src_path.tag(this)->to_string(true, false, this));
tag_bldr->insertPath(src_path);
}
}
@ -858,28 +848,21 @@ Genclks::recordSrcPaths(Clock *gclk)
while (path_iter.hasNext()) {
Path *path = path_iter.next();
const ClockEdge *src_clk_edge = path->clkEdge(this);
if (src_clk_edge
&& matchesSrcFilter(path, gclk)) {
if (src_clk_edge && matchesSrcFilter(path, gclk)) {
const EarlyLate *early_late = path->minMax(this);
const RiseFall *src_clk_rf = src_clk_edge->transition();
const RiseFall *rf = path->transition(this);
bool inverting_path = (rf != src_clk_rf);
size_t path_index = srcPathIndex(rf, path->minMax(this));
Path &src_path = src_paths[path_index];
if ((!divide_by_1
|| (inverting_path == invert))
&& (!has_edges
|| src_clk_rf == gclk->masterClkEdgeTr(rf))
if ((!divide_by_1 || (inverting_path == invert))
&& (!has_edges || src_clk_rf == gclk->masterClkEdgeTr(rf))
&& (src_path.isNull()
|| delayGreater(path->arrival(),
src_path.arrival(),
early_late,
|| delayGreater(path->arrival(), src_path.arrival(), early_late,
this))) {
debugPrint(debug_, "genclk", 2, " %s insertion %s %s %s",
network_->pathName(gclk_pin),
early_late->to_string().c_str(),
rf->shortName(),
delayAsString(path->arrival(), this));
debugPrint(debug_, "genclk", 2, " {} insertion {} {} {}",
network_->pathName(gclk_pin), early_late->to_string(),
rf->shortName(), delayAsString(path->arrival(), this));
src_path = *path;
}
}
@ -905,19 +888,17 @@ Genclks::recordSrcPaths(Clock *gclk)
}
}
// Don't warn if the master clock is ideal.
if (!found_src_paths
&& gclk->masterClk()
&& gclk->masterClk()->isPropagated())
report_->warn(1062, "generated clock %s source pin %s missing paths from master clock %s.",
gclk->name(),
network_->pathName(gclk_pin),
gclk->masterClk()->name());
if (!found_src_paths && gclk->masterClk() && gclk->masterClk()->isPropagated())
report_->warn(
1062,
"generated clock {} source pin {} missing paths from master clock {}.",
gclk->name(), network_->pathName(gclk_pin), gclk->masterClk()->name());
}
deleteGenclkSrcPaths(gclk);
}
void
Genclks:: deleteGenclkSrcPaths(Clock *gclk)
Genclks::deleteGenclkSrcPaths(Clock *gclk)
{
GenclkInfo *genclk_info = genclkInfo(gclk);
GenClkInsertionSearchPred srch_pred(gclk, genclk_info, mode_);
@ -938,13 +919,10 @@ Genclks::matchesSrcFilter(Path *path,
{
Tag *tag = path->tag(this);
const ExceptionStateSet *states = tag->states();
if (tag->isGenClkSrcPath()
&& states) {
if (tag->isGenClkSrcPath() && states) {
for (ExceptionState *state : *states) {
ExceptionPath *except = state->exception();
if (except->isFilter()
&& state->nextThru() == nullptr
&& except->to()
if (except->isFilter() && state->nextThru() == nullptr && except->to()
&& except->to()->matches(gclk))
return true;
}
@ -958,8 +936,7 @@ Genclks::srcPath(const Path *clk_path) const
const Pin *src_pin = clk_path->pin(this);
const ClockEdge *clk_edge = clk_path->clkEdge(this);
const EarlyLate *early_late = clk_path->minMax(this);
return srcPath(clk_edge->clock(), src_pin,
clk_edge->transition(), early_late);
return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), early_late);
}
const Path *
@ -967,8 +944,7 @@ Genclks::srcPath(const ClockEdge *clk_edge,
const Pin *src_pin,
const MinMax *min_max) const
{
return srcPath(clk_edge->clock(), src_pin,
clk_edge->transition(), min_max);
return srcPath(clk_edge->clock(), src_pin, clk_edge->transition(), min_max);
}
const Path *
@ -1016,9 +992,7 @@ ClockPinPairLess::operator()(const ClockPinPair &pair1,
int clk_index2 = clk2->index();
const Pin *pin1 = pair1.second;
const Pin *pin2 = pair2.second;
return (clk_index1 < clk_index2
|| (clk_index1 == clk_index2
&& pin1 < pin2));
return (clk_index1 < clk_index2 || (clk_index1 == clk_index2 && pin1 < pin2));
}
class ClockPinPairHash
@ -1054,8 +1028,7 @@ ClockPinPairEqual::operator()(const ClockPinPair &pair1,
const ClockPinPair &pair2) const
{
return pair1.first == pair2.first
&& pair1.second == pair2.second;
return pair1.first == pair2.first && pair1.second == pair2.second;
}
} // namespace
} // namespace sta

View File

@ -76,7 +76,7 @@ Latches::latchRequired(const Path *data_path,
time_given_to_startpoint = 0.0;
}
else if (enable_path && disable_path) {
debugPrint(debug_, "latch", 1, "latch %s",
debugPrint(debug_, "latch", 1, "latch {}",
sdc_network_->pathName(data_path->pin(this)));
Delay open_latency, latency_diff, max_borrow;
float nom_pulse_width, open_uncertainty;
@ -107,7 +107,7 @@ Latches::latchRequired(const Path *data_path,
open_latency,
this);
enable_arrival = delaySum(enable_arrival, open_crpr, this);
debugPrint(debug_, "latch", 1, "data %s enable %s",
debugPrint(debug_, "latch", 1, "data {} enable {}",
delayAsString(data_arrival, this),
delayAsString(enable_arrival, this));
if (delayLessEqual(data_arrival, enable_arrival, this)) {
@ -155,7 +155,7 @@ Latches::latchRequired(const Path *data_path,
adjusted_data_arrival = data_arrival;
time_given_to_startpoint = 0.0;
}
debugPrint(debug_, "latch", 2, "req %s borrow %s time_given %s adj_arrival %s",
debugPrint(debug_, "latch", 2, "req {} borrow {} time_given {} adj_arrival {}",
delayAsString(required, this),
delayAsString(borrow, this),
delayAsString(time_given_to_startpoint, this),
@ -226,12 +226,12 @@ Latches::latchBorrowInfo(const Path *data_path,
open_crpr = 0.0;
crpr_diff = 0.0;
}
debugPrint(debug_, "latch", 2, "nom_width %s open_lat %s lat_diff %s open_uncert %s",
debugPrint(debug_, "latch", 2, "nom_width {} open_lat {} lat_diff {} open_uncert {}",
delayAsString(nom_pulse_width, this),
delayAsString(open_latency, this),
delayAsString(latency_diff, this),
delayAsString(open_uncertainty, this));
debugPrint(debug_, "latch", 2, "open_crpr %s crpr_diff %s open_uncert %s max_borrow %s",
debugPrint(debug_, "latch", 2, "open_crpr {} crpr_diff {} open_uncert {} max_borrow {}",
delayAsString(open_crpr, this),
delayAsString(crpr_diff, this),
delayAsString(open_uncertainty, this),

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "Levelize.hh"
@ -162,8 +162,7 @@ Levelize::findRoots()
while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next();
if (isRoot(vertex)) {
debugPrint(debug_, "levelize", 2, "root %s%s",
vertex->to_string(this).c_str(),
debugPrint(debug_, "levelize", 2, "root {}{}", vertex->to_string(this),
hasFanout(vertex) ? " fanout" : "");
roots_.insert(vertex);
}
@ -174,9 +173,8 @@ Levelize::findRoots()
if (hasFanout(root))
fanout_roots++;
}
debugPrint(debug_, "levelize", 1, "Found %zu roots %zu with fanout",
roots_.size(),
fanout_roots);
debugPrint(debug_, "levelize", 1, "Found {} roots {} with fanout",
roots_.size(), fanout_roots);
}
}
@ -200,14 +198,11 @@ bool
Levelize::searchThru(Edge *edge)
{
const TimingRole *role = edge->role();
return !role->isTimingCheck()
&& role != TimingRole::latchDtoQ()
&& !edge->isDisabledLoop()
// Register/latch preset/clr edges are disabled by default.
&& !(role == TimingRole::regSetClr()
&& !variables_->presetClrArcsEnabled())
&& !(edge->isBidirectInstPath()
&& !variables_->bidirectInstPathsEnabled());
return !role->isTimingCheck() && role != TimingRole::latchDtoQ()
&& !edge->isDisabledLoop()
// Register/latch preset/clr edges are disabled by default.
&& !(role == TimingRole::regSetClr() && !variables_->presetClrArcsEnabled())
&& !(edge->isBidirectInstPath() && !variables_->bidirectInstPathsEnabled());
}
bool
@ -271,7 +266,7 @@ Levelize::findBackEdges(EdgeSeq &path,
EdgeSet back_edges;
while (!stack.empty()) {
VertexEdgeIterPair vertex_iter = stack.top();
const auto& [vertex, edge_iter] = vertex_iter;
const auto &[vertex, edge_iter] = vertex_iter;
if (edge_iter->hasNext()) {
Edge *edge = edge_iter->next();
if (searchThru(edge)) {
@ -282,7 +277,7 @@ Levelize::findBackEdges(EdgeSeq &path,
path.push_back(edge);
stack.emplace(to_vertex, new VertexOutEdgeIterator(to_vertex, graph_));
}
else if (to_vertex->visited2()) { // on path
else if (to_vertex->visited2()) { // on path
// Found a back edge (loop).
recordLoop(edge, path);
back_edges.insert(edge);
@ -327,7 +322,7 @@ Levelize::findCycleBackEdges()
back_edge_count += back_edges.size();
}
}
debugPrint(debug_, "levelize", 1, "Found %zu cycle back edges", back_edge_count);
debugPrint(debug_, "levelize", 1, "Found {} cycle back edges", back_edge_count);
}
// Find vertices in cycles that are were not accessible from roots.
@ -350,7 +345,7 @@ VertexSeq
Levelize::findTopologicalOrder()
{
Stats stats(debug_, report_);
std::map<Vertex*, int> in_degree;
std::map<Vertex *, int> in_degree;
VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) {
@ -368,12 +363,13 @@ Levelize::findTopologicalOrder()
const Pin *pin = vertex->pin();
if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin)
&& !vertex->isBidirectDriver()) {
Vertex *to_vertex = graph_->pinDrvrVertex(pin);;
Vertex *to_vertex = graph_->pinDrvrVertex(pin);
;
in_degree[to_vertex] += 1;
}
}
std::deque<Vertex*> queue;
std::deque<Vertex *> queue;
for (Vertex *root : roots_)
queue.push_back(root);
@ -412,14 +408,14 @@ Levelize::findTopologicalOrder()
while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next();
if (in_degree[vertex] != 0)
debugPrint(debug_, "levelize", 2, "topological sort missing %s",
vertex->to_string(this).c_str());
debugPrint(debug_, "levelize", 2, "topological sort missing {}",
vertex->to_string(this));
}
}
if (debug_->check("levelize", 3)) {
report_->reportLine("Topological sort");
report_->report("Topological sort");
for (Vertex *vertex : topo_order)
report_->reportLine("%s", vertex->to_string(this).c_str());
report_->report("{}", vertex->to_string(this));
}
stats.report("Levelize topological sort");
return topo_order;
@ -429,9 +425,8 @@ void
Levelize::recordLoop(Edge *edge,
EdgeSeq &path)
{
debugPrint(debug_, "levelize", 2, "Loop edge %s (%s)",
edge->to_string(this).c_str(),
edge->role()->to_string().c_str());
debugPrint(debug_, "levelize", 2, "Loop edge {} ({})",
edge->to_string(this), edge->role()->to_string());
EdgeSeq *loop_edges = loopEdges(path, edge);
GraphLoop *loop = new GraphLoop(loop_edges);
loops_.push_back(loop);
@ -460,14 +455,12 @@ Levelize::loopEdges(EdgeSeq &path,
if (from_pin == loop_pin)
copy = true;
if (copy) {
debugPrint(debug_, "loop", 2, " %s",
edge->to_string(this).c_str());
debugPrint(debug_, "loop", 2, " {}", edge->to_string(this));
loop_edges->push_back(edge);
loop_edges_.insert(edge);
}
}
debugPrint(debug_, "loop", 2, " %s",
closing_edge->to_string(this).c_str());
debugPrint(debug_, "loop", 2, " {}", closing_edge->to_string(this));
loop_edges->push_back(closing_edge);
loop_edges_.insert(closing_edge);
return loop_edges;
@ -479,8 +472,8 @@ Levelize::reportPath(EdgeSeq &path) const
bool first_edge = true;
for (Edge *edge : path) {
if (first_edge)
report_->reportLine(" %s", edge->from(graph_)->to_string(this).c_str());
report_->reportLine(" %s", edge->to(graph_)->to_string(this).c_str());
report_->report(" {}", edge->from(graph_)->to_string(this));
report_->report(" {}", edge->to(graph_)->to_string(this));
first_edge = false;
}
}
@ -499,16 +492,16 @@ Levelize::assignLevels(VertexSeq &topo_sorted)
Edge *edge = edge_iter.next();
Vertex *to_vertex = edge->to(graph_);
if (searchThru(edge))
setLevel(to_vertex, std::max(to_vertex->level(),
vertex->level() + level_space_));
setLevel(to_vertex,
std::max(to_vertex->level(), vertex->level() + level_space_));
}
// Levelize bidirect driver as if it was a fanout of the bidirect load.
const Pin *pin = vertex->pin();
if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin)
&& !vertex->isBidirectDriver()) {
Vertex *to_vertex = graph_->pinDrvrVertex(pin);
setLevel(to_vertex, std::max(to_vertex->level(),
vertex->level() + level_space_));
setLevel(to_vertex,
std::max(to_vertex->level(), vertex->level() + level_space_));
}
}
}
@ -528,12 +521,9 @@ Levelize::ensureLatchLevels()
Vertex *to = edge->to(graph_);
if (from->level() == to->level()) {
Level adjusted_level = from->level() + level_space_;
debugPrint(debug_, "levelize", 2, "latch %s %d (adjusted %d) -> %s %d",
from->to_string(this).c_str(),
from->level(),
adjusted_level,
to->to_string(this).c_str(),
to->level());
debugPrint(debug_, "levelize", 2, "latch {} {} (adjusted {}) -> {} {}",
from->to_string(this), from->level(), adjusted_level,
to->to_string(this), to->level());
setLevel(from, adjusted_level);
}
}
@ -541,12 +531,11 @@ Levelize::ensureLatchLevels()
}
void
Levelize::setLevel(Vertex *vertex,
Levelize::setLevel(Vertex *vertex,
Level level)
{
debugPrint(debug_, "levelize", 3, "set level %s %d",
vertex->to_string(this).c_str(),
level);
debugPrint(debug_, "levelize", 3, "set level {} {}",
vertex->to_string(this), level);
vertex->setLevel(level);
max_level_ = std::max(level, max_level_);
if (level >= Graph::vertex_level_max)
@ -576,8 +565,8 @@ void
Levelize::relevelizeFrom(Vertex *vertex)
{
if (levelized_) {
debugPrint(debug_, "levelize", 1, "level invalid from %s",
vertex->to_string(this).c_str());
debugPrint(debug_, "levelize", 1, "level invalid from {}",
vertex->to_string(this));
relevelize_from_.insert(vertex);
levels_valid_ = false;
}
@ -586,10 +575,9 @@ Levelize::relevelizeFrom(Vertex *vertex)
void
Levelize::deleteEdgeBefore(Edge *edge)
{
if (levelized_
&& loop_edges_.contains(edge)) {
debugPrint(debug_, "levelize", 2, "delete loop edge %s",
edge->to_string(this).c_str());
if (levelized_ && loop_edges_.contains(edge)) {
debugPrint(debug_, "levelize", 2, "delete loop edge {}",
edge->to_string(this));
disabled_loop_edges_.erase(edge);
// Relevelize if a loop edge is removed. Incremental levelization
// fails because the DFS path will be missing.
@ -610,9 +598,9 @@ void
Levelize::relevelize()
{
for (Vertex *vertex : relevelize_from_) {
debugPrint(debug_, "levelize", 2, "relevelize from %s",
vertex->to_string(this).c_str());
if (isRoot(vertex))
debugPrint(debug_, "levelize", 2, "relevelize from {}",
vertex->to_string(this));
if (isRoot(vertex))
roots_.insert(vertex);
VertexSet path_vertices = makeVertexSet(this);
EdgeSeq path;
@ -646,8 +634,8 @@ Levelize::visit(Vertex *vertex,
// Back edges form feedback loops.
recordLoop(edge, path);
else if (to_vertex->level() <= level)
visit(to_vertex, edge, level+level_space, level_space,
path_vertices, path);
visit(to_vertex, edge, level + level_space, level_space, path_vertices,
path);
}
const TimingRole *role = edge->role();
@ -668,8 +656,8 @@ Levelize::visit(Vertex *vertex,
&& !vertex->isBidirectDriver()) {
Vertex *to_vertex = graph_->pinDrvrVertex(from_pin);
if (to_vertex->level() <= level)
visit(to_vertex, nullptr, level+level_space, level_space,
path_vertices, path);
visit(to_vertex, nullptr, level + level_space, level_space, path_vertices,
path);
}
path_vertices.erase(vertex);
if (from)
@ -683,12 +671,11 @@ Levelize::isDisabledLoop(Edge *edge) const
}
void
Levelize::setLevelIncr(Vertex *vertex,
Levelize::setLevelIncr(Vertex *vertex,
Level level)
{
debugPrint(debug_, "levelize", 2, "set level %s %d",
vertex->to_string(this).c_str(),
level);
debugPrint(debug_, "levelize", 2, "set level {} {}",
vertex->to_string(this), level);
if (vertex->level() != level) {
if (observer_)
observer_->levelChangedBefore(vertex);
@ -715,11 +702,9 @@ Levelize::checkLevels()
&& from_level >= level
// Loops with no entry edges are all level zero.
&& !(from_level == 0 && level == 0))
report_->warn(617, "level check failed %s %d -> %s %d",
from_vertex->name(network_),
from_vertex->level(),
vertex->name(network_),
level);
report_->warn(617, "level check failed {} {} -> {} {}",
from_vertex->name(network_), from_vertex->level(),
vertex->name(network_), level);
}
}
}
@ -731,18 +716,14 @@ GraphLoop::GraphLoop(EdgeSeq *edges) :
{
}
GraphLoop::~GraphLoop()
{
delete edges_;
}
GraphLoop::~GraphLoop() { delete edges_; }
bool
GraphLoop::isCombinational() const
{
for (Edge *edge : *edges_) {
const TimingRole *role = edge->role();
if (!(role == TimingRole::wire()
|| role == TimingRole::combinational()
if (!(role == TimingRole::wire() || role == TimingRole::combinational()
|| role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable()))
return false;
@ -758,10 +739,10 @@ GraphLoop::report(const StaState *sta) const
bool first_edge = true;
for (Edge *edge : *edges_) {
if (first_edge)
report->reportLine(" %s", edge->from(graph)->to_string(sta).c_str());
report->reportLine(" %s", edge->to(graph)->to_string(graph).c_str());
report->report(" {}", edge->from(graph)->to_string(sta));
report->report(" {}", edge->to(graph)->to_string(sta));
first_edge = false;
}
}
} // namespace
} // namespace sta

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "MakeTimingModel.hh"
@ -75,7 +75,8 @@ MakeTimingModel::MakeTimingModel(const char *lib_name,
scene_(scene),
cell_(nullptr),
min_max_(MinMax::max()),
lib_builder_(new LibertyBuilder(debug_, report_)),
lib_builder_(new LibertyBuilder(debug_,
report_)),
tbl_template_index_(1),
sdc_(scene->sdc()),
sdc_backup_(nullptr),
@ -84,10 +85,7 @@ MakeTimingModel::MakeTimingModel(const char *lib_name,
scenes_.insert(scene_);
}
MakeTimingModel::~MakeTimingModel()
{
delete lib_builder_;
}
MakeTimingModel::~MakeTimingModel() { delete lib_builder_; }
LibertyLibrary *
MakeTimingModel::makeTimingModel()
@ -107,7 +105,7 @@ MakeTimingModel::makeTimingModel()
cell_->finish(false, report_, debug_);
restoreSdc();
return library_;
}
@ -193,9 +191,8 @@ MakeTimingModel::makePorts()
int from_index = network_->fromIndex(port);
int to_index = network_->toIndex(port);
BusDcl *bus_dcl = library_->makeBusDcl(port_name, from_index, to_index);
LibertyPort *lib_port = lib_builder_->makeBusPort(cell_, port_name,
from_index, to_index,
bus_dcl);
LibertyPort *lib_port =
lib_builder_->makeBusPort(cell_, port_name, from_index, to_index, bus_dcl);
lib_port->setDirection(network_->direction(port));
PortMemberIterator *member_iter = network_->memberIterator(port);
while (member_iter->hasNext()) {
@ -223,8 +220,7 @@ MakeTimingModel::checkClock(Clock *clk)
{
for (const Pin *pin : clk->leafPins()) {
if (!network_->isTopLevelPort(pin))
report_->warn(1355, "clock %s pin %s is inside model block.",
clk->name(),
report_->warn(1355, "clock {} pin {} is inside model block.", clk->name(),
network_->pathName(pin));
}
}
@ -235,7 +231,7 @@ class MakeEndTimingArcs : public PathEndVisitor
{
public:
MakeEndTimingArcs(Sta *sta);
MakeEndTimingArcs(const MakeEndTimingArcs&) = default;
MakeEndTimingArcs(const MakeEndTimingArcs &) = default;
~MakeEndTimingArcs() override {}
PathEndVisitor *copy() const override;
void visit(PathEnd *path_end) override;
@ -273,8 +269,7 @@ MakeEndTimingArcs::visit(PathEnd *path_end)
const Sdc *sdc = src_path->sdc(sta_);
const Clock *src_clk = src_path->clock(sta_);
const ClockEdge *tgt_clk_edge = path_end->targetClkEdge(sta_);
if (src_clk == sdc->defaultArrivalClock()
&& tgt_clk_edge) {
if (src_clk == sdc->defaultArrivalClock() && tgt_clk_edge) {
Network *network = sta_->network();
Debug *debug = sta_->debug();
const MinMax *min_max = path_end->minMax(sta_);
@ -285,13 +280,10 @@ MakeEndTimingArcs::visit(PathEnd *path_end)
? delaySum(delayDiff(data_delay, clk_latency, sta_), check_margin, sta_)
: delaySum(delayDiff(clk_latency, data_delay, sta_), check_margin, sta_);
float delay1 = delayAsFloat(margin, MinMax::max(), sta_);
debugPrint(debug, "make_timing_model", 2, "%s -> %s clock %s %s %s %s",
input_rf_->shortName(),
network->pathName(src_path->pin(sta_)),
tgt_clk_edge->name(),
path_end->typeName(),
min_max->to_string().c_str(),
delayAsString(margin, sta_));
debugPrint(debug, "make_timing_model", 2, "{} -> {} clock {} {} {} {}",
input_rf_->shortName(), network->pathName(src_path->pin(sta_)),
tgt_clk_edge->name(), path_end->typeName(),
min_max->to_string(), delayAsString(margin, sta_));
if (debug->check("make_timing_model", 3))
sta_->reportPathEnd(path_end);
@ -335,15 +327,14 @@ MakeTimingModel::findTimingFromInput(Port *input_port)
OutputPinDelays output_delays;
for (const RiseFall *input_rf : RiseFall::range()) {
const RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth();
sta_->setInputDelay(input_pin, input_rf1,
sdc_->defaultArrivalClock(),
sdc_->defaultArrivalClockEdge()->transition(),
nullptr, false, false, MinMaxAll::all(), true, 0.0, sdc_);
sta_->setInputDelay(input_pin, input_rf1, sdc_->defaultArrivalClock(),
sdc_->defaultArrivalClockEdge()->transition(), nullptr,
false, false, MinMaxAll::all(), true, 0.0, sdc_);
PinSet *from_pins = new PinSet(network_);
from_pins->insert(input_pin);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
input_rf1, sdc_);
ExceptionFrom *from =
sta_->makeExceptionFrom(from_pins, nullptr, nullptr, input_rf1, sdc_);
search_->findFilteredArrivals(from, nullptr, nullptr, false, false);
end_visitor.setInputRf(input_rf);
@ -354,8 +345,7 @@ MakeTimingModel::findTimingFromInput(Port *input_port)
findOutputDelays(input_rf, output_delays);
search_->deleteFilteredArrivals();
sta_->removeInputDelay(input_pin, input_rf1,
sdc_->defaultArrivalClock(),
sta_->removeInputDelay(input_pin, input_rf1, sdc_->defaultArrivalClock(),
sdc_->defaultArrivalClockEdge()->transition(),
MinMaxAll::all(), sdc_);
}
@ -395,7 +385,7 @@ void
MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin,
const ClockEdgeDelays &clk_margins)
{
for (const auto& [clk_edge, margins] : clk_margins) {
for (const auto &[clk_edge, margins] : clk_margins) {
for (const MinMax *min_max : MinMax::range()) {
bool setup = (min_max == MinMax::max());
TimingArcAttrsPtr attrs = nullptr;
@ -404,16 +394,14 @@ MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin,
bool exists;
margins.value(input_rf, min_max, margin, exists);
if (exists) {
debugPrint(debug_, "make_timing_model", 2, "%s %s %s -> clock %s %s",
sta_->network()->pathName(input_pin),
input_rf->shortName(),
min_max == MinMax::max() ? "setup" : "hold",
clk_edge->name(),
debugPrint(debug_, "make_timing_model", 2, "{} {} {} -> clock {} {}",
sta_->network()->pathName(input_pin), input_rf->shortName(),
min_max == MinMax::max() ? "setup" : "hold", clk_edge->name(),
delayAsString(margin, sta_));
ScaleFactorType scale_type = setup
? ScaleFactorType::setup
: ScaleFactorType::hold;
TimingModel *check_model = makeScalarCheckModel(margin, scale_type, input_rf);
ScaleFactorType scale_type =
setup ? ScaleFactorType::setup : ScaleFactorType::hold;
TimingModel *check_model =
makeScalarCheckModel(margin, scale_type, input_rf);
if (attrs == nullptr)
attrs = std::make_shared<TimingArcAttrs>();
attrs->setModel(input_rf, check_model);
@ -425,12 +413,10 @@ MakeTimingModel::makeSetupHoldTimingArcs(const Pin *input_pin,
LibertyPort *clk_port = modelPort(clk_pin);
if (clk_port) {
const RiseFall *clk_rf = clk_edge->transition();
const TimingRole *role = setup
? TimingRole::setup()
: TimingRole::hold();
lib_builder_->makeFromTransitionArcs(cell_, clk_port,
input_port, nullptr,
clk_rf, role, attrs);
const TimingRole *role =
setup ? TimingRole::setup() : TimingRole::hold();
lib_builder_->makeFromTransitionArcs(cell_, clk_port, input_port,
nullptr, clk_rf, role, attrs);
}
}
}
@ -442,7 +428,7 @@ void
MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin,
OutputPinDelays &output_pin_delays)
{
for (const auto& [output_pin, output_delays] : output_pin_delays) {
for (const auto &[output_pin, output_delays] : output_pin_delays) {
TimingArcAttrsPtr attrs = nullptr;
for (const RiseFall *output_rf : RiseFall::range()) {
const MinMax *min_max = MinMax::max();
@ -450,11 +436,9 @@ MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin,
bool exists;
output_delays.delays.value(output_rf, min_max, delay, exists);
if (exists) {
debugPrint(debug_, "make_timing_model", 2, "%s -> %s %s delay %s",
network_->pathName(input_pin),
network_->pathName(output_pin),
output_rf->shortName(),
delayAsString(delay, sta_));
debugPrint(debug_, "make_timing_model", 2, "{} -> {} {} delay {}",
network_->pathName(input_pin), network_->pathName(output_pin),
output_rf->shortName(), delayAsString(delay, sta_));
TimingModel *gate_model = makeGateModelTable(output_pin, delay, output_rf);
if (attrs == nullptr)
attrs = std::make_shared<TimingArcAttrs>();
@ -465,8 +449,8 @@ MakeTimingModel::makeInputOutputTimingArcs(const Pin *input_pin,
LibertyPort *output_port = modelPort(output_pin);
LibertyPort *input_port = modelPort(input_pin);
attrs->setTimingSense(output_delays.timingSense());
lib_builder_->makeCombinationalArcs(cell_, input_port, output_port,
true, true, attrs);
lib_builder_->makeCombinationalArcs(cell_, input_port, output_port, true, true,
attrs);
}
}
}
@ -479,7 +463,7 @@ MakeTimingModel::findClkedOutputPaths()
{
InstancePinIterator *output_iter = network_->pinIterator(network_->topInstance());
while (output_iter->hasNext()) {
Pin *output_pin = output_iter->next();
Pin *output_pin = output_iter->next();
if (network_->direction(output_pin)->isOutput()) {
ClockEdgeDelays clk_delays;
LibertyPort *output_port = modelPort(output_pin);
@ -493,11 +477,10 @@ MakeTimingModel::findClkedOutputPaths()
const MinMax *min_max = path->minMax(sta_);
Arrival delay = path->arrival();
RiseFallMinMax &delays = clk_delays[clk_edge];
delays.mergeValue(output_rf, min_max,
delayAsFloat(delay, min_max, sta_));
delays.mergeValue(output_rf, min_max, delayAsFloat(delay, min_max, sta_));
}
}
for (const auto& [clk_edge, delays] : clk_delays) {
for (const auto &[clk_edge, delays] : clk_delays) {
for (const Pin *clk_pin : clk_edge->clock()->pins()) {
LibertyPort *clk_port = modelPort(clk_pin);
if (clk_port) {
@ -505,16 +488,16 @@ MakeTimingModel::findClkedOutputPaths()
TimingArcAttrsPtr attrs = nullptr;
for (const RiseFall *output_rf : RiseFall::range()) {
float delay = delays.value(output_rf, min_max_) - clk_edge->time();
TimingModel *gate_model = makeGateModelTable(output_pin, delay, output_rf);
TimingModel *gate_model =
makeGateModelTable(output_pin, delay, output_rf);
if (attrs == nullptr)
attrs = std::make_shared<TimingArcAttrs>();
attrs->setModel(output_rf, gate_model);
}
if (attrs) {
lib_builder_->makeFromTransitionArcs(cell_, clk_port,
output_port, nullptr,
clk_rf, TimingRole::regClkToQ(),
attrs);
lib_builder_->makeFromTransitionArcs(cell_, clk_port, output_port,
nullptr, clk_rf,
TimingRole::regClkToQ(), attrs);
}
}
}
@ -545,8 +528,10 @@ MakeTimingModel::findClkTreeDelays()
for (const Clock *clk : *clks) {
ClkDelays delays = sta_->findClkDelays(clk, scene_, true);
for (const MinMax *min_max : MinMax::range()) {
makeClkTreePaths(lib_port, min_max, TimingSense::positive_unate, delays);
makeClkTreePaths(lib_port, min_max, TimingSense::negative_unate, delays);
makeClkTreePaths(lib_port, min_max, TimingSense::positive_unate,
delays);
makeClkTreePaths(lib_port, min_max, TimingSense::negative_unate,
delays);
}
}
}
@ -564,15 +549,14 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port,
{
TimingArcAttrsPtr attrs = nullptr;
for (const RiseFall *clk_rf : RiseFall::range()) {
const RiseFall *end_rf = (sense == TimingSense::positive_unate)
? clk_rf
: clk_rf->opposite();
const RiseFall *end_rf =
(sense == TimingSense::positive_unate) ? clk_rf : clk_rf->opposite();
Path clk_path;
Delay insertion, delay, latency;
float lib_clk_delay;
bool exists;
delays.delay(clk_rf, end_rf, min_max, insertion, delay,
lib_clk_delay, latency, clk_path, exists);
delays.delay(clk_rf, end_rf, min_max, insertion, delay, lib_clk_delay, latency,
clk_path, exists);
if (exists) {
TimingModel *model = makeGateModelScalar(delay, end_rf);
if (attrs == nullptr)
@ -583,8 +567,8 @@ MakeTimingModel::makeClkTreePaths(LibertyPort *lib_port,
if (attrs) {
attrs->setTimingSense(sense);
const TimingRole *role = (min_max == MinMax::min())
? TimingRole::clockTreePathMin()
: TimingRole::clockTreePathMax();
? TimingRole::clockTreePathMin()
: TimingRole::clockTreePathMax();
lib_builder_->makeClockTreePathArcs(cell_, lib_port, role, attrs);
}
}
@ -669,12 +653,11 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin,
const Pin *gate_in_pin = network_->findPin(drvr_inst, gate_in_port);
if (gate_in_pin) {
Vertex *gate_in_vertex = graph_->pinLoadVertex(gate_in_pin);
Slew in_slew = graph_->slew(gate_in_vertex,
drvr_arc->fromEdge()->asRiseFall(),
ap_index);
Slew in_slew = graph_->slew(
gate_in_vertex, drvr_arc->fromEdge()->asRiseFall(), ap_index);
float in_slew1 = delayAsFloat(in_slew);
GateTableModel *drvr_gate_model = drvr_arc->gateTableModel(scene_,
min_max_);
GateTableModel *drvr_gate_model =
drvr_arc->gateTableModel(scene_, min_max_);
if (drvr_gate_model) {
float output_load_cap = graph_delay_calc_->loadCap(output_pin,
scene_,
@ -704,15 +687,17 @@ MakeTimingModel::makeGateModelTable(const Pin *output_pin,
}
FloatSeq axis_values = drvr_axis_values;
TableAxisPtr load_axis =
std::make_shared<TableAxis>(TableAxisVariable::total_output_net_capacitance,
std::move(axis_values));
TableAxisPtr load_axis = std::make_shared<TableAxis>(
TableAxisVariable::total_output_net_capacitance,
std::move(axis_values));
TablePtr delay_table = std::make_shared<Table>(load_values, load_axis);
TablePtr slew_table = std::make_shared<Table>(slew_values, load_axis);
TablePtr delay_table =
std::make_shared<Table>(load_values, load_axis);
TablePtr slew_table =
std::make_shared<Table>(slew_values, load_axis);
TableTemplate *model_template = ensureTableTemplate(drvr_template,
load_axis);
TableTemplate *model_template =
ensureTableTemplate(drvr_template, load_axis);
TableModel *delay_model = new TableModel(delay_table, model_template,
ScaleFactorType::cell, rf);
TableModels *delay_models = new TableModels(delay_model);
@ -745,8 +730,8 @@ MakeTimingModel::ensureTableTemplate(const TableTemplate *drvr_template,
std::string template_name = "template_";
template_name += std::to_string(tbl_template_index_++);
model_template = library_->makeTableTemplate(template_name,
TableTemplateType::delay);
model_template =
library_->makeTableTemplate(template_name, TableTemplateType::delay);
model_template->setAxis1(load_axis);
template_map_[drvr_template] = model_template;
}
@ -757,13 +742,16 @@ const TableAxis *
MakeTimingModel::loadCapacitanceAxis(const TableModel *table)
{
if (table->axis1()
&& table->axis1()->variable() == TableAxisVariable::total_output_net_capacitance)
&& table->axis1()->variable()
== TableAxisVariable::total_output_net_capacitance)
return table->axis1();
else if (table->axis2()
&& table->axis2()->variable() == TableAxisVariable::total_output_net_capacitance)
&& table->axis2()->variable()
== TableAxisVariable::total_output_net_capacitance)
return table->axis2();
else if (table->axis3()
&& table->axis3()->variable() == TableAxisVariable::total_output_net_capacitance)
&& table->axis3()->variable()
== TableAxisVariable::total_output_net_capacitance)
return table->axis3();
else
return nullptr;
@ -781,9 +769,9 @@ TimingSense
OutputDelays::timingSense() const
{
if (rf_path_exists[RiseFall::riseIndex()][RiseFall::riseIndex()]
&& rf_path_exists[RiseFall::fallIndex()][RiseFall::fallIndex()]
&& !rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()]
&& !rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()])
&& rf_path_exists[RiseFall::fallIndex()][RiseFall::fallIndex()]
&& !rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()]
&& !rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()])
return TimingSense::positive_unate;
else if (rf_path_exists[RiseFall::riseIndex()][RiseFall::fallIndex()]
&& rf_path_exists[RiseFall::fallIndex()][RiseFall::riseIndex()]
@ -799,4 +787,4 @@ OutputDelays::timingSense() const
return TimingSense::none;
}
} // namespace
} // namespace sta

View File

@ -1,25 +1,25 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
//
// This notice may not be removed or altered from any source distribution.
#include "Path.hh"
@ -129,8 +129,7 @@ Path::init(Vertex *vertex,
{
const Graph *graph = sta->graph();
vertex_id_ = graph->id(vertex);
tag_index_ = tag_index_null,
prev_path_ = nullptr;
tag_index_ = tag_index_null, prev_path_ = nullptr;
prev_arc_idx_ = 0;
arrival_ = arrival;
required_ = 0.0;
@ -144,8 +143,7 @@ Path::init(Vertex *vertex,
{
const Graph *graph = sta->graph();
vertex_id_ = graph->id(vertex);
tag_index_ = tag->index(),
prev_path_ = nullptr;
tag_index_ = tag->index(), prev_path_ = nullptr;
prev_arc_idx_ = 0;
arrival_ = 0.0;
required_ = 0.0;
@ -160,8 +158,7 @@ Path::init(Vertex *vertex,
{
const Graph *graph = sta->graph();
vertex_id_ = graph->id(vertex);
tag_index_ = tag->index(),
prev_path_ = nullptr;
tag_index_ = tag->index(), prev_path_ = nullptr;
prev_arc_idx_ = 0;
arrival_ = arrival;
required_ = 0.0;
@ -178,8 +175,7 @@ Path::init(Vertex *vertex,
const StaState *sta)
{
const Graph *graph = sta->graph();
tag_index_ = tag->index(),
prev_path_ = prev_path;
tag_index_ = tag->index(), prev_path_ = prev_path;
if (prev_path) {
prev_edge_id_ = graph->id(prev_edge);
prev_arc_idx_ = prev_arc->index();
@ -199,12 +195,12 @@ Path::to_string(const StaState *sta) const
if (isNull())
return "null path";
else
return stringPrintTmp("%s %s %s/%s %d",
vertex(sta)->to_string(sta).c_str(),
transition(sta)->shortName(),
scene(sta)->name().c_str(),
minMax(sta)->to_string().c_str(),
tagIndex(sta));
return sta::format("{} {} {}/{} {}",
vertex(sta)->to_string(sta),
transition(sta)->shortName(),
scene(sta)->name(),
minMax(sta)->to_string(),
tagIndex(sta));
}
bool
@ -221,7 +217,7 @@ Path::vertex(const StaState *sta) const
const Edge *edge = graph->edge(prev_edge_id_);
return edge->to(graph);
}
else
else
return graph->vertex(vertex_id_);
}
@ -404,7 +400,7 @@ Path::prevArc(const StaState *sta) const
TimingArcSet *arc_set = edge->timingArcSet();
return arc_set->findTimingArc(prev_arc_idx_);
}
else
else
return nullptr;
}
@ -415,7 +411,7 @@ Path::prevEdge(const StaState *sta) const
const Graph *graph = sta->graph();
return graph->edge(prev_edge_id_);
}
else
else
return nullptr;
}
@ -448,8 +444,7 @@ void
Path::checkPrevPath(const StaState *sta) const
{
if (prev_path_ && prev_path_->isNull())
sta->report()->reportLine("path %s prev path is null.",
to_string(sta).c_str());
sta->report()->report("path {} prev path is null.", to_string(sta));
if (prev_path_ && !prev_path_->isNull()) {
Graph *graph = sta->graph();
Edge *edge = prevEdge(sta);
@ -457,10 +452,9 @@ Path::checkPrevPath(const StaState *sta) const
Vertex *prev_edge_vertex = edge->from(graph);
if (prev_vertex != prev_edge_vertex) {
Network *network = sta->network();
sta->report()->reportLine("path %s prev path corrupted %s vs %s.",
to_string(sta).c_str(),
prev_vertex->name(network),
prev_edge_vertex->name(network));
sta->report()->report("path {} prev path corrupted {} vs {}.", to_string(sta),
prev_vertex->name(network),
prev_edge_vertex->name(network));
}
}
}
@ -478,14 +472,14 @@ Path::tgtClkMinMax(const StaState *sta) const
{
const MinMax *min_max = minMax(sta);
switch (mode(sta)->sdc()->analysisType()) {
case AnalysisType::single:
case AnalysisType::bc_wc:
return min_max;
case AnalysisType::ocv:
return min_max->opposite();
default:
// suppress gcc warning
return min_max;
case AnalysisType::single:
case AnalysisType::bc_wc:
return min_max;
case AnalysisType::ocv:
return min_max->opposite();
default:
// suppress gcc warning
return min_max;
}
}
////////////////////////////////////////////////////////////////
@ -579,8 +573,7 @@ Path::cmpClk(const Path *path1,
else
return 1;
}
else if (clk_edge1 == nullptr
&& clk_edge2 == nullptr)
else if (clk_edge1 == nullptr && clk_edge2 == nullptr)
return 0;
else if (clk_edge2)
return -1;
@ -594,11 +587,10 @@ Path::equal(const Path *path1,
const StaState *sta)
{
return (path1 == nullptr && path2 == nullptr)
|| (path1
&& path2
&& path1->vertexId(sta) == path2->vertexId(sta)
// Tag equal implies transition and path ap equal.
&& path1->tagIndex(sta) == path2->tagIndex(sta));
|| (path1 && path2
&& path1->vertexId(sta) == path2->vertexId(sta)
// Tag equal implies transition and path ap equal.
&& path1->tagIndex(sta) == path2->tagIndex(sta));
}
////////////////////////////////////////////////////////////////
@ -779,12 +771,9 @@ VertexPathIterator::findNext()
Path *path = &paths_[path_index_++];
if (filtered_) {
const Tag *tag = path->tag(search_);
if ((scene_ == nullptr
|| path->scene(search_) == scene_)
&& (rf_ == nullptr
|| tag->rfIndex() == rf_->index())
&& (min_max_ == nullptr
|| path->minMax(search_) == min_max_)) {
if ((scene_ == nullptr || path->scene(search_) == scene_)
&& (rf_ == nullptr || tag->rfIndex() == rf_->index())
&& (min_max_ == nullptr || path->minMax(search_) == min_max_)) {
next_ = path;
return;
}
@ -797,9 +786,7 @@ VertexPathIterator::findNext()
next_ = nullptr;
}
VertexPathIterator::~VertexPathIterator()
{
}
VertexPathIterator::~VertexPathIterator() {}
bool
VertexPathIterator::hasNext()
@ -815,4 +802,4 @@ VertexPathIterator::next()
return path;
}
} // namespace
} // namespace sta

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