Merge pull request #357 from The-OpenROAD-Project-staging/sta_update_latest_0505

Sta update latest 0505
This commit is contained in:
Matt Liberty 2026-05-12 16:40:41 +00:00 committed by GitHub
commit 2110ea71a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 611 additions and 579 deletions

4
BUILD
View File

@ -120,7 +120,7 @@ tcl_srcs = [
"tcl/CmdArgs.tcl",
"tcl/Property.tcl",
"tcl/Sta.tcl",
"tcl/Variables.tcl",
"sdc/Variables.tcl",
"tcl/Splash.tcl",
"graph/Graph.tcl",
"liberty/Liberty.tcl",
@ -147,7 +147,7 @@ exported_tcl = [
"tcl/Property.tcl",
"tcl/Splash.tcl",
"tcl/Sta.tcl",
"tcl/Variables.tcl",
"sdc/Variables.tcl",
"sdc/Sdc.tcl",
"sdf/Sdf.tcl",
"search/Search.tcl",

View File

@ -257,7 +257,6 @@ set(STA_TCL_FILES
tcl/Property.tcl
tcl/Sta.tcl
tcl/Splash.tcl
tcl/Variables.tcl
dcalc/DelayCalc.tcl
graph/Graph.tcl
liberty/Liberty.tcl
@ -267,6 +266,7 @@ set(STA_TCL_FILES
parasitics/Parasitics.tcl
power/Power.tcl
sdc/Sdc.tcl
sdc/Variables.tcl
sdf/Sdf.tcl
search/Search.tcl
spice/WriteSpice.tcl

View File

@ -127,6 +127,7 @@ class ArnoldiDelayCalc : public LumpedCapDelayCalc
{
public:
ArnoldiDelayCalc(StaState *sta);
ArnoldiDelayCalc(const ArnoldiDelayCalc &dcalc);
~ArnoldiDelayCalc() override;
ArcDelayCalc *copy() override;
std::string_view name() const override { return "arnoldi"; }
@ -262,10 +263,20 @@ ArnoldiDelayCalc::ArnoldiDelayCalc(StaState *sta) :
_slewV = (double*)malloc(_pinNmax * sizeof(double));
}
ArnoldiDelayCalc::ArnoldiDelayCalc(const ArnoldiDelayCalc &dcalc) :
LumpedCapDelayCalc(dcalc),
reduce_(new ArnoldiReduce(this)),
delay_work_(delay_work_create())
{
_pinNmax = dcalc._pinNmax;
_delayV = (double*)malloc(_pinNmax * sizeof(double));
_slewV = (double*)malloc(_pinNmax * sizeof(double));
}
ArcDelayCalc *
ArnoldiDelayCalc::copy()
{
return new ArnoldiDelayCalc(this);
return new ArnoldiDelayCalc(*this);
}
ArnoldiDelayCalc::~ArnoldiDelayCalc()

View File

@ -60,12 +60,23 @@ CcsCeffDelayCalc::CcsCeffDelayCalc(StaState *sta) :
{
}
CcsCeffDelayCalc::~CcsCeffDelayCalc() { delete table_dcalc_; }
CcsCeffDelayCalc::CcsCeffDelayCalc(const CcsCeffDelayCalc &dcalc) :
LumpedCapDelayCalc(dcalc),
watch_pin_values_(dcalc.network_),
capacitance_unit_(dcalc.capacitance_unit_),
table_dcalc_(makeDmpCeffElmoreDelayCalc(this))
{
}
CcsCeffDelayCalc::~CcsCeffDelayCalc()
{
delete table_dcalc_;
}
ArcDelayCalc *
CcsCeffDelayCalc::copy()
{
return new CcsCeffDelayCalc(this);
return new CcsCeffDelayCalc(*this);
}
ArcDcalcResult

View File

@ -39,6 +39,7 @@ class CcsCeffDelayCalc : public LumpedCapDelayCalc,
{
public:
CcsCeffDelayCalc(StaState *sta);
CcsCeffDelayCalc(const CcsCeffDelayCalc &dcalc);
~CcsCeffDelayCalc() override;
ArcDelayCalc *copy() override;
std::string_view name() const override { return "ccs_ceff"; }

View File

@ -68,6 +68,17 @@ static const char *dmp_func_index_strings[] = {"y20", "y50", "Ipi"};
static double
exp2(double x);
static double
gateModelRd(const LibertyCell *cell,
const GateTableModel *gate_model,
const RiseFall *rf,
double in_slew,
double c2,
double c1,
const Pvt *pvt);
////////////////////////////////////////////////////////////////
class DmpError : public Exception
{
public:
@ -78,156 +89,13 @@ private:
std::string what_;
};
static double
gateModelRd(const LibertyCell *cell,
const GateTableModel *gate_model,
const RiseFall *rf,
double in_slew,
double c2,
double c1,
const Pvt *pvt);
////////////////////////////////////////////////////////////////
// Base class for Dartu/Menezes/Pileggi algorithm.
// Derived classes handle different cases of zero values in the Pi model.
class DmpAlg : public StaState
DmpError::DmpError(std::string_view what) :
what_(what)
{
public:
DmpAlg(int nr_order,
StaState *sta);
~DmpAlg() override = default;
virtual std::string_view name() = 0;
// Set driver model and pi model parameters for delay calculation.
virtual void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1);
virtual std::pair<double, double> gateDelaySlew() = 0;
virtual std::pair<double, double> loadDelaySlew(const Pin *load_pin,
double elmore);
double ceff() { return ceff_; }
//sta::print(stdout, "DmpError {}\n", what);
}
// Given x_ as a vector of input parameters, fill fvec_ with the
// equations evaluated at x_ and fjac_ with the jabobian evaluated at x_.
virtual void evalDmpEqns() = 0;
// Output response to vs(t) ramp driving pi model load (vo, dvo_dt).
std::pair<double, double> Vo(double t);
// Load response to driver waveform (vl, dvl/dt).
std::pair<double, double> Vl(double t);
protected:
void luDecomp();
void luSolve();
void newtonRaphson();
// Find driver parameters t0, delta_t, Ceff.
void findDriverParams(double ceff);
std::pair<double, double> gateCapDelaySlew(double ceff);
std::tuple<double, double, double> gateDelays(double ceff);
// Partial derivatives of y(t) jacobian (dydt0, dyddt, dydcl).
std::tuple<double, double, double> dy(double t,
double t0,
double dt,
double cl);
double y0dt(double t,
double cl);
double y0dcl(double t,
double cl);
void showX();
void showFvec();
void showJacobian();
std::pair<double, double> findDriverDelaySlew();
double findVoCrossing(double vth,
double t_lower,
double t_upper);
void showVo();
double findVlCrossing(double vth,
double t_lower,
double t_upper);
void showVl();
void fail(std::string_view reason);
// Output response to vs(t) ramp driving capacitive load (y, t1).
std::pair<double, double> y(double t,
double t0,
double dt,
double cl);
// Output response to unit ramp driving capacitive load.
double y0(double t,
double cl);
// Output response to unit ramp driving pi model load.
// Unit ramp output at pi load (vo, dvo_dt).
virtual std::pair<double, double> V0(double t) = 0;
// Upper bound on time that vo crosses vh.
virtual double voCrossingUpperBound() = 0;
// Load responce to driver unit ramp.
// Unit ramp load response (vl, dvl_dt).
virtual std::pair<double, double> Vl0(double t) = 0;
// Upper bound on time that vl crosses vh.
double vlCrossingUpperBound();
// Inputs to the delay calculator.
const LibertyCell *drvr_cell_;
const LibertyLibrary *drvr_library_;
const Pvt *pvt_;
const GateTableModel *gate_model_;
double in_slew_;
double c2_{0.0};
double rpi_{0.0};
double c1_{0.0};
double rd_;
// Logic threshold (percentage of supply voltage).
double vth_;
// Slew lower limit (percentage of supply voltage).
double vl_;
// Slew upper limit (percentage of supply voltage).
double vh_;
// Table slews are scaled by slew_derate to get
// measured slews from vl to vh.
double slew_derate_;
// Driver parameters calculated by this algorithm.
double t0_;
double dt_;
double ceff_;
// Driver parameter Newton-Raphson state.
int nr_order_;
static constexpr int max_nr_order_ = 3;
std::array<double, max_nr_order_> x_;
std::array<double, max_nr_order_> fvec_;
std::array<std::array<double, max_nr_order_>, max_nr_order_> fjac_;
std::array<double, max_nr_order_> scale_;
std::array<double, max_nr_order_> p_;
std::array<int, max_nr_order_> index_;
// Driver slew used to check load delay.
double drvr_slew_;
double vo_delay_;
// True if the driver parameters are valid for finding the load delays.
bool driver_valid_;
// Load rspf elmore delay.
double elmore_;
double p3_;
// Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0).
static constexpr double driver_param_tol_ = .01;
// Waveform threshold crossing time tolerance (1.0 = 100%).
static constexpr double vth_time_tol_ = .01;
// Max iterations for findRoot.
static constexpr int find_root_max_iter_ = 20;
static inline int newton_raphson_max_iter_ = 100;
// A small number used by luDecomp.
static constexpr double tiny_double_ = 1.0e-20;
};
////////////////////////////////////////////////////////////////
DmpAlg::DmpAlg(int nr_order,
StaState *sta) :
@ -593,33 +461,6 @@ DmpAlg::fail(std::string_view reason)
////////////////////////////////////////////////////////////////
// Capacitive load.
class DmpCap : public DmpAlg
{
public:
DmpCap(StaState *sta);
std::string_view name() override { return "cap"; }
void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
std::pair<double, double> loadDelaySlew(const Pin *,
double elmore) override;
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
};
DmpCap::DmpCap(StaState *sta) :
DmpAlg(1,
sta)
@ -692,53 +533,6 @@ DmpCap::Vl0(double)
////////////////////////////////////////////////////////////////
// No non-zero pi model parameters, two poles, one zero
class DmpPi : public DmpAlg
{
public:
DmpPi(StaState *sta);
std::string_view name() override { return "Pi"; }
void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
private:
void findDriverParamsPi();
double ipiIceff(double t0,
double dt,
double ceff_time,
double ceff);
// Poles/zero.
double p1_{0.0};
double p2_{0.0};
double z1_{0.0};
// Residues.
double k0_{0.0};
double k1_{0.0};
double k2_{0.0};
double k3_{0.0};
double k4_{0.0};
// Ipi coefficients.
double A_{0.0};
double B_{0.0};
double D_{0.0};
};
DmpPi::DmpPi(StaState *sta) :
DmpAlg(3,
sta)
@ -940,18 +734,6 @@ DmpPi::voCrossingUpperBound()
////////////////////////////////////////////////////////////////
// Capacitive load, so Ceff is known.
// Solve for t0, delta t.
class DmpOnePole : public DmpAlg
{
public:
DmpOnePole(StaState *sta);
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
};
DmpOnePole::DmpOnePole(StaState *sta) :
DmpAlg(2,
sta)
@ -1000,40 +782,6 @@ DmpOnePole::voCrossingUpperBound()
////////////////////////////////////////////////////////////////
// C2 = 0, one pole, one zero.
class DmpZeroC2 : public DmpOnePole
{
public:
DmpZeroC2(StaState *sta);
std::string_view name() override { return "c2=0"; }
void init(const LibertyLibrary *drvr_library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
protected:
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
double voCrossingUpperBound() override;
private:
// Pole/zero.
double p1_{0.0};
double z1_{0.0};
// Residues.
double k0_{0.0};
double k1_{0.0};
double k2_{0.0};
double k3_{0.0};
};
DmpZeroC2::DmpZeroC2(StaState *sta) :
DmpOnePole(sta)
{
@ -1260,19 +1008,12 @@ bool DmpCeffDelayCalc::unsuppored_model_warned_ = false;
DmpCeffDelayCalc::DmpCeffDelayCalc(StaState *sta) :
LumpedCapDelayCalc(sta),
dmp_cap_(new DmpCap(sta)),
dmp_pi_(new DmpPi(sta)),
dmp_zero_c2_(new DmpZeroC2(sta))
dmp_cap_(sta),
dmp_pi_(sta),
dmp_zero_c2_(sta)
{
}
DmpCeffDelayCalc::~DmpCeffDelayCalc()
{
delete dmp_cap_;
delete dmp_pi_;
delete dmp_zero_c2_;
}
ArcDcalcResult
DmpCeffDelayCalc::gateDelay(const Pin *drvr_pin,
const TimingArc *arc,
@ -1362,15 +1103,15 @@ DmpCeffDelayCalc::setCeffAlgorithm(const LibertyLibrary *drvr_library,
|| rpi < rd * 1e-3
// c1/Rpi can be ignored.
|| (c1 == 0.0 || c1 < c2 * 1e-3 || rpi == 0.0))
dmp_alg_ = dmp_cap_;
dmp_alg_ = &dmp_cap_;
else if (c2 < c1 * 1e-3)
dmp_alg_ = dmp_zero_c2_;
dmp_alg_ = &dmp_zero_c2_;
else
// The full monty.
dmp_alg_ = dmp_pi_;
dmp_alg_ = &dmp_pi_;
}
else
dmp_alg_ = dmp_cap_;
dmp_alg_ = &dmp_cap_;
dmp_alg_->init(drvr_library, drvr_cell, pvt, gate_model, rf, rd, in_slew,
c2, rpi, c1);
debugPrint(debug_, "dmp_ceff", 3,
@ -1473,15 +1214,9 @@ void
DmpCeffDelayCalc::copyState(const StaState *sta)
{
StaState::copyState(sta);
dmp_cap_->copyState(sta);
dmp_pi_->copyState(sta);
dmp_zero_c2_->copyState(sta);
}
DmpError::DmpError(std::string_view what) :
what_(what)
{
//sta::print(stdout, "DmpError {}\n", what);
dmp_cap_.copyState(sta);
dmp_pi_.copyState(sta);
dmp_zero_c2_.copyState(sta);
}
// This saves about 2.5% in overall run time on designs with SPEF.

View File

@ -32,19 +32,275 @@
namespace sta {
class DmpAlg;
class DmpCap;
class DmpPi;
class DmpZeroC2;
class GateTableModel;
// Base class for Dartu/Menezes/Pileggi algorithm.
// Derived classes handle different cases of zero values in the Pi model.
class DmpAlg : public StaState
{
public:
DmpAlg(int nr_order,
StaState *sta);
~DmpAlg() override = default;
virtual std::string_view name() = 0;
// Set driver model and pi model parameters for delay calculation.
virtual void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1);
virtual std::pair<double, double> gateDelaySlew() = 0;
virtual std::pair<double, double> loadDelaySlew(const Pin *load_pin,
double elmore);
double ceff() { return ceff_; }
// Given x_ as a vector of input parameters, fill fvec_ with the
// equations evaluated at x_ and fjac_ with the jabobian evaluated at x_.
virtual void evalDmpEqns() = 0;
// Output response to vs(t) ramp driving pi model load (vo, dvo_dt).
std::pair<double, double> Vo(double t);
// Load response to driver waveform (vl, dvl/dt).
std::pair<double, double> Vl(double t);
protected:
void luDecomp();
void luSolve();
void newtonRaphson();
// Find driver parameters t0, delta_t, Ceff.
void findDriverParams(double ceff);
std::pair<double, double> gateCapDelaySlew(double ceff);
std::tuple<double, double, double> gateDelays(double ceff);
// Partial derivatives of y(t) jacobian (dydt0, dyddt, dydcl).
std::tuple<double, double, double> dy(double t,
double t0,
double dt,
double cl);
double y0dt(double t,
double cl);
double y0dcl(double t,
double cl);
void showX();
void showFvec();
void showJacobian();
std::pair<double, double> findDriverDelaySlew();
double findVoCrossing(double vth,
double t_lower,
double t_upper);
void showVo();
double findVlCrossing(double vth,
double t_lower,
double t_upper);
void showVl();
void fail(std::string_view reason);
// Output response to vs(t) ramp driving capacitive load (y, t1).
std::pair<double, double> y(double t,
double t0,
double dt,
double cl);
// Output response to unit ramp driving capacitive load.
double y0(double t,
double cl);
// Output response to unit ramp driving pi model load.
// Unit ramp output at pi load (vo, dvo_dt).
virtual std::pair<double, double> V0(double t) = 0;
// Upper bound on time that vo crosses vh.
virtual double voCrossingUpperBound() = 0;
// Load responce to driver unit ramp.
// Unit ramp load response (vl, dvl_dt).
virtual std::pair<double, double> Vl0(double t) = 0;
// Upper bound on time that vl crosses vh.
double vlCrossingUpperBound();
// Inputs to the delay calculator.
const LibertyCell *drvr_cell_;
const LibertyLibrary *drvr_library_;
const Pvt *pvt_;
const GateTableModel *gate_model_;
double in_slew_;
double c2_{0.0};
double rpi_{0.0};
double c1_{0.0};
double rd_;
// Logic threshold (percentage of supply voltage).
double vth_;
// Slew lower limit (percentage of supply voltage).
double vl_;
// Slew upper limit (percentage of supply voltage).
double vh_;
// Table slews are scaled by slew_derate to get
// measured slews from vl to vh.
double slew_derate_;
// Driver parameters calculated by this algorithm.
double t0_;
double dt_;
double ceff_;
// Driver parameter Newton-Raphson state.
int nr_order_;
static constexpr int max_nr_order_ = 3;
std::array<double, max_nr_order_> x_;
std::array<double, max_nr_order_> fvec_;
std::array<std::array<double, max_nr_order_>, max_nr_order_> fjac_;
std::array<double, max_nr_order_> scale_;
std::array<double, max_nr_order_> p_;
std::array<int, max_nr_order_> index_;
// Driver slew used to check load delay.
double drvr_slew_;
double vo_delay_;
// True if the driver parameters are valid for finding the load delays.
bool driver_valid_;
// Load rspf elmore delay.
double elmore_;
double p3_;
// Tolerance (as a scale of value) for driver parameters (Ceff, delta t, t0).
static constexpr double driver_param_tol_ = .01;
// Waveform threshold crossing time tolerance (1.0 = 100%).
static constexpr double vth_time_tol_ = .01;
// Max iterations for findRoot.
static constexpr int find_root_max_iter_ = 20;
static inline int newton_raphson_max_iter_ = 100;
// A small number used by luDecomp.
static constexpr double tiny_double_ = 1.0e-20;
};
// Capacitive load.
class DmpCap : public DmpAlg
{
public:
DmpCap(StaState *sta);
std::string_view name() override { return "cap"; }
void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
std::pair<double, double> loadDelaySlew(const Pin *,
double elmore) override;
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
};
// No non-zero pi model parameters, two poles, one zero
class DmpPi : public DmpAlg
{
public:
DmpPi(StaState *sta);
std::string_view name() override { return "Pi"; }
void init(const LibertyLibrary *library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
private:
void findDriverParamsPi();
double ipiIceff(double t0,
double dt,
double ceff_time,
double ceff);
// Poles/zero.
double p1_{0.0};
double p2_{0.0};
double z1_{0.0};
// Residues.
double k0_{0.0};
double k1_{0.0};
double k2_{0.0};
double k3_{0.0};
double k4_{0.0};
// Ipi coefficients.
double A_{0.0};
double B_{0.0};
double D_{0.0};
};
// Capacitive load, so Ceff is known.
// Solve for t0, delta t.
class DmpOnePole : public DmpAlg
{
public:
DmpOnePole(StaState *sta);
void evalDmpEqns() override;
protected:
double voCrossingUpperBound() override;
};
// C2 = 0, one pole, one zero.
class DmpZeroC2 : public DmpOnePole
{
public:
DmpZeroC2(StaState *sta);
std::string_view name() override { return "c2=0"; }
void init(const LibertyLibrary *drvr_library,
const LibertyCell *drvr_cell,
const Pvt *pvt,
const GateTableModel *gate_model,
const RiseFall *rf,
double rd,
double in_slew,
double c2,
double rpi,
double c1) override;
std::pair<double, double> gateDelaySlew() override;
protected:
std::pair<double, double> V0(double t) override;
std::pair<double, double> Vl0(double t) override;
double voCrossingUpperBound() override;
private:
// Pole/zero.
double p1_{0.0};
double z1_{0.0};
// Residues.
double k0_{0.0};
double k1_{0.0};
double k2_{0.0};
double k3_{0.0};
};
// Delay calculator using Dartu/Menezes/Pileggi effective capacitance
// algorithm for RSPF loads.
class DmpCeffDelayCalc : public LumpedCapDelayCalc
{
public:
DmpCeffDelayCalc(StaState *sta);
~DmpCeffDelayCalc() override;
bool reduceSupported() const override { return true; }
ArcDcalcResult gateDelay(const Pin *drvr_pin,
const TimingArc *arc,
@ -94,10 +350,9 @@ protected:
private:
// Dmp algorithms for each special pi model case.
// These objects are reused to minimize make/deletes.
DmpCap *dmp_cap_;
DmpPi *dmp_pi_;
DmpZeroC2 *dmp_zero_c2_;
DmpCap dmp_cap_;
DmpPi dmp_pi_;
DmpZeroC2 dmp_zero_c2_;
DmpAlg *dmp_alg_{nullptr};
};

View File

@ -77,7 +77,7 @@ DmpCeffElmoreDelayCalc::DmpCeffElmoreDelayCalc(StaState *sta) :
ArcDelayCalc *
DmpCeffElmoreDelayCalc::copy()
{
return new DmpCeffElmoreDelayCalc(this);
return new DmpCeffElmoreDelayCalc(*this);
}
ArcDcalcResult
@ -213,7 +213,7 @@ DmpCeffTwoPoleDelayCalc::DmpCeffTwoPoleDelayCalc(StaState *sta) :
ArcDelayCalc *
DmpCeffTwoPoleDelayCalc::copy()
{
return new DmpCeffTwoPoleDelayCalc(this);
return new DmpCeffTwoPoleDelayCalc(*this);
}
Parasitic *

View File

@ -292,6 +292,7 @@ class FindVertexDelays : public VertexVisitor
{
public:
FindVertexDelays(GraphDelayCalc *graph_delay_calc1);
FindVertexDelays(const FindVertexDelays &find_vertex_delays);
~FindVertexDelays() override;
void visit(Vertex *vertex) override;
VertexVisitor *copy() const override;
@ -308,6 +309,11 @@ FindVertexDelays::FindVertexDelays(GraphDelayCalc *graph_delay_calc) :
{
}
FindVertexDelays::FindVertexDelays(const FindVertexDelays &find_vertex_delays) :
FindVertexDelays(find_vertex_delays.graph_delay_calc_)
{
}
FindVertexDelays::~FindVertexDelays()
{
delete arc_delay_calc_;
@ -318,7 +324,7 @@ FindVertexDelays::copy() const
{
// Copy StaState::arc_delay_calc_ because it needs separate state
// for each thread.
return new FindVertexDelays(graph_delay_calc_);
return new FindVertexDelays(*this);
}
void

View File

@ -54,7 +54,7 @@ LumpedCapDelayCalc::LumpedCapDelayCalc(StaState *sta) :
ArcDelayCalc *
LumpedCapDelayCalc::copy()
{
return new LumpedCapDelayCalc(this);
return new LumpedCapDelayCalc(*this);
}
Parasitic *

View File

@ -42,7 +42,7 @@ UnitDelayCalc::UnitDelayCalc(StaState *sta) :
ArcDelayCalc *
UnitDelayCalc::copy()
{
return new UnitDelayCalc(this);
return new UnitDelayCalc(*this);
}
Parasitic *

View File

@ -4,6 +4,13 @@ OpenSTA Timing Analyzer Release Notes
This file summarizes user visible changes for each release.
See ApiChangeLog.txt for changes to the STA api.
2026/05/01
----------
The write_sdc command supports a -mode argument.
write_sdc [-mode mode]
2026/03/23
----------

View File

@ -63,7 +63,6 @@ public:
{
}
// Copy constructor
BoundedHeap(const BoundedHeap& other) :
heap_(other.heap_),
max_size_(other.max_size_),

View File

@ -327,7 +327,7 @@ protected:
bool has_downstream_clk_pin_:1;
bool visited1_:1;
bool visited2_:1;
bool has_sim_value_;
bool has_sim_value_:1;
private:
friend class Graph;

View File

@ -199,9 +199,7 @@ public:
PropertyValue(ClockSet *value);
PropertyValue(ConstPathSeq *value);
PropertyValue(PwrActivity *value);
// Copy constructor.
PropertyValue(const PropertyValue &value);
// Move constructor.
PropertyValue(PropertyValue &&value) noexcept;
~PropertyValue();
Type type() const { return type_; }

View File

@ -738,15 +738,15 @@ protected:
Vertex *to_vertex);
// Return false to stop visiting.
[[nodiscard]] bool visitArc(const Pin *from_pin,
Vertex *from_vertex,
const RiseFall *from_rf,
Path *from_path,
Edge *edge,
TimingArc *arc,
const Pin *to_pin,
Vertex *to_vertex,
const MinMax *min_max,
const Mode *mode);
Vertex *from_vertex,
const RiseFall *from_rf,
Path *from_path,
Edge *edge,
TimingArc *arc,
const Pin *to_pin,
Vertex *to_vertex,
const MinMax *min_max,
const Mode *mode);
// This calls visit below with everything required to make to_path.
// Return false to stop visiting.
virtual bool visitFromPath(const Pin *from_pin,
@ -770,6 +770,7 @@ class ArrivalVisitor : public PathVisitor
{
public:
ArrivalVisitor(const StaState *sta);
ArrivalVisitor(const ArrivalVisitor &arrival_visitor);
~ArrivalVisitor() override;
// Initialize the visitor.
void init(bool always_to_endpoints,
@ -797,9 +798,6 @@ public:
TagGroupBldr *tagBldr() const { return tag_bldr_; }
protected:
ArrivalVisitor(bool always_to_endpoints,
SearchPred *pred,
const StaState *sta);
void init0();
void enqueueRefPinInputDelays(const Pin *ref_pin,
const Sdc *sdc);
@ -842,6 +840,7 @@ class RequiredVisitor : public PathVisitor
{
public:
RequiredVisitor(const StaState *sta);
RequiredVisitor(const RequiredVisitor &required_visitor);
~RequiredVisitor() override;
VertexVisitor *copy() const override;
void visit(Vertex *vertex) override;
@ -862,10 +861,7 @@ public:
const MinMax *min_max) override;
protected:
RequiredVisitor(bool make_tag_cache,
const StaState *sta);
RequiredCmp *required_cmp_;
RequiredCmp required_cmp_;
VisitPathEnds *visit_path_ends_;
};

View File

@ -138,7 +138,7 @@ public:
void setCmdScene(Scene *scene);
SceneSeq makeSceneSeq(Scene *scene) const;
Mode *cmdMode() const { return cmd_scene_->mode(); }
Mode *cmdMode() const { return cmd_mode_; }
const std::string &cmdModeName();
void setCmdMode(std::string_view mode_name);
Mode *findMode(std::string_view mode_name) const;
@ -1042,6 +1042,13 @@ public:
const Scene *scene,
const MinMax *min_max,
int digits);
void writeSdc(std::string_view filename,
std::string_view mode_name,
bool leaf,
bool native,
int digits,
bool gzip,
bool no_timestamp);
void writeSdc(const Sdc *sdc,
std::string_view filename,
// Map hierarchical pins and instances to leaf pins and instances.
@ -1610,6 +1617,7 @@ protected:
void checkLibrarayPocv();
Scene *cmd_scene_{nullptr};
Mode *cmd_mode_{nullptr};
CmdNamespace cmd_namespace_{CmdNamespace::sdc};
Instance *current_instance_{nullptr};
SceneNameMap scene_name_map_;

View File

@ -575,6 +575,7 @@ public:
PropActivityVisitor(Power *power,
const Mode *mode,
BfsFwdIterator *bfs);
PropActivityVisitor(const PropActivityVisitor &visitor);
VertexVisitor *copy() const override;
void visit(Vertex *vertex) override;
InstanceSet &visitedRegs() { return visited_regs_; }
@ -606,10 +607,15 @@ PropActivityVisitor::PropActivityVisitor(Power *power,
{
}
PropActivityVisitor::PropActivityVisitor(const PropActivityVisitor &visitor) :
PropActivityVisitor(visitor.power_, visitor.mode_, visitor.bfs_)
{
}
VertexVisitor *
PropActivityVisitor::copy() const
{
return new PropActivityVisitor(power_, mode_, bfs_);
return new PropActivityVisitor(*this);
}
void

View File

@ -94,15 +94,15 @@ private:
void
write_sdc_cmd(std::string filename,
bool leaf,
bool compatible,
int digits,
bool gzip,
bool no_timestamp)
std::string mode_name,
bool leaf,
bool compatible,
int digits,
bool gzip,
bool no_timestamp)
{
Sta *sta = Sta::sta();
const Sdc *sdc = sta->cmdSdc();
sta->writeSdc(sdc, filename, leaf, compatible, digits, gzip, no_timestamp);
sta->writeSdc(filename, mode_name, leaf, compatible, digits, gzip, no_timestamp);
}
void

View File

@ -42,31 +42,38 @@ proc_redirect read_sdc {
check_argc_eq1 "read_sdc" $args
set echo [info exists flags(-echo)]
set filename [file nativename [lindex $args 0]]
set mode_name {}
if { [info exists keys(-mode)] } {
set mode_name $keys(-mode)
}
set prev_mode [cmd_mode_name]
try {
set_mode_cmd $mode_name
include_file $filename $echo 0
} finally {
if { $prev_mode != "default" } {
set_mode_cmd $prev_mode
set prev_mode [cmd_mode]
try {
set_cmd_mode $mode_name
include_file $filename $echo 0
} finally {
if { $prev_mode != "default" } {
set_cmd_mode $prev_mode
}
}
} else {
include_file $filename $echo 0
}
}
################################################################
define_cmd_args "write_sdc" \
{[-map_hpins] [-digits digits] [-gzip] [-no_timestamp] filename}
{[-mode mode] [-map_hpins] [-digits digits] [-gzip] [-no_timestamp] filename}
proc write_sdc { args } {
parse_key_args "write_sdc" args keys {-digits -significant_digits} \
parse_key_args "write_sdc" args keys {-mode -digits} \
flags {-map_hpins -compatible -gzip -no_timestamp}
check_argc_eq1 "write_sdc" $args
set mode [cmd_mode]
if { [info exists keys(-mode)] } {
set mode $keys(-mode)
}
set digits 4
if { [info exists keys(-digits)] } {
set digits $keys(-digits)
@ -78,7 +85,7 @@ proc write_sdc { args } {
set no_timestamp [info exists flags(-no_timestamp)]
set map_hpins [info exists flags(-map_hpins)]
set native [expr ![info exists flags(-compatible)]]
write_sdc_cmd $filename $map_hpins $native $digits $gzip $no_timestamp
write_sdc_cmd $filename $mode $map_hpins $native $digits $gzip $no_timestamp
}
################################################################

View File

@ -180,7 +180,7 @@ define_cmd_args "write_sdf" \
proc_redirect write_sdf {
parse_key_args "write_sdf" args \
keys {-corner -scene -divider -digits -significant_digits} \
keys {-corner -scene -divider -digits} \
flags {-include_typ -gzip -no_timestamp -no_version}
check_argc_eq1 "write_sdf" $args
set scene [parse_scene keys]

View File

@ -107,7 +107,6 @@ GatedClk::isGatedClkEnable(Vertex *enable_vertex,
}
}
}
}
}

View File

@ -693,17 +693,11 @@ public:
BfsFwdIterator *insert_iter,
GenclkInfo *genclk_info,
const Mode *mode);
GenclkSrcArrivalVisitor(const GenclkSrcArrivalVisitor &visitor);
VertexVisitor *copy() const override;
void visit(Vertex *vertex) override;
protected:
GenclkSrcArrivalVisitor(Clock *gclk,
BfsFwdIterator *insert_iter,
GenclkInfo *genclk_info,
bool always_to_endpoints,
SearchPred *pred,
const Mode *mode);
Clock *gclk_;
BfsFwdIterator *insert_iter_;
GenclkInfo *genclk_info_;
@ -728,29 +722,22 @@ GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk,
{
}
// Copy constructor.
GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(Clock *gclk,
BfsFwdIterator *insert_iter,
GenclkInfo *genclk_info,
bool always_to_endpoints,
SearchPred *pred,
const Mode *mode) :
ArrivalVisitor(always_to_endpoints, pred, this),
gclk_(gclk),
insert_iter_(insert_iter),
genclk_info_(genclk_info),
srch_pred_(gclk, genclk_info, this),
mode_(mode),
sdc_(mode->sdc()),
genclks_(mode->genclks())
GenclkSrcArrivalVisitor::GenclkSrcArrivalVisitor(const GenclkSrcArrivalVisitor &visitor) :
ArrivalVisitor(visitor),
gclk_(visitor.gclk_),
insert_iter_(visitor.insert_iter_),
genclk_info_(visitor.genclk_info_),
srch_pred_(visitor.gclk_, visitor.genclk_info_, this),
mode_(visitor.mode_),
sdc_(visitor.sdc_),
genclks_(visitor.genclks_)
{
}
VertexVisitor *
GenclkSrcArrivalVisitor::copy() const
{
return new GenclkSrcArrivalVisitor(gclk_, insert_iter_, genclk_info_,
always_to_endpoints_, pred_, mode_);
return new GenclkSrcArrivalVisitor(*this);
}
void

View File

@ -246,6 +246,7 @@ public:
bool unique_pins,
bool unique_edges,
PathEnum *path_enum);
PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor);
VertexVisitor *copy() const override;
void visitFaninPathsThru(Path *before_div,
Vertex *prev_vertex,
@ -327,11 +328,16 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end,
{
}
PathEnumFaninVisitor::PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor) :
PathEnumFaninVisitor(visitor.path_end_, visitor.before_div_, visitor.unique_pins_,
visitor.unique_edges_, visitor.path_enum_)
{
}
VertexVisitor *
PathEnumFaninVisitor::copy() const
{
return new PathEnumFaninVisitor(path_end_, before_div_, unique_pins_,
unique_edges_, path_enum_);
return new PathEnumFaninVisitor(*this);
}
void

View File

@ -995,7 +995,7 @@ MakeEndpointPathEnds::~MakeEndpointPathEnds()
VertexVisitor *
MakeEndpointPathEnds::copy() const
{
return new MakeEndpointPathEnds(path_end_visitor_, scenes_, min_max_, sta_);
return new MakeEndpointPathEnds(*this);
}
void

View File

@ -1090,14 +1090,11 @@ ArrivalVisitor::ArrivalVisitor(const StaState *sta) :
init(true, false, nullptr);
}
// Copy constructor.
ArrivalVisitor::ArrivalVisitor(bool always_to_endpoints,
SearchPred *pred,
const StaState *sta) :
PathVisitor(pred, true, sta)
ArrivalVisitor::ArrivalVisitor(const ArrivalVisitor &arrival_visitor) :
PathVisitor(arrival_visitor.pred_, true, &arrival_visitor)
{
init0();
init(always_to_endpoints, false, pred);
init(arrival_visitor.always_to_endpoints_, false, arrival_visitor.pred_);
}
void
@ -1122,7 +1119,7 @@ ArrivalVisitor::init(bool always_to_endpoints,
VertexVisitor *
ArrivalVisitor::copy() const
{
return new ArrivalVisitor(always_to_endpoints_, pred_, this);
return new ArrivalVisitor(*this);
}
void
@ -1966,7 +1963,10 @@ PathVisitor::PathVisitor(SearchPred *pred,
{
}
PathVisitor::~PathVisitor() { delete tag_cache_; }
PathVisitor::~PathVisitor()
{
delete tag_cache_;
}
void
PathVisitor::visitFaninPaths(Vertex *to_vertex)
@ -2775,23 +2775,19 @@ Search::reportArrivals(Vertex *vertex,
bool report_prev = false;
std::string prev_str;
if (report_prev) {
prev_str = "prev ";
Path *prev_path = path->prevPath();
if (prev_path) {
prev_str += prev_path->to_string(this);
prev_str += " ";
const Edge *prev_edge = path->prevEdge(this);
TimingArc *arc = path->prevArc(this);
prev_str += prev_edge->from(graph_)->to_string(this);
prev_str += " ";
prev_str += arc->fromEdge()->to_string();
prev_str += " -> ";
prev_str += prev_edge->to(graph_)->to_string(this);
prev_str += " ";
prev_str += arc->toEdge()->to_string();
prev_str = sta::format("prev {} {} {} -> {} {}",
prev_path->to_string(this),
prev_edge->from(graph_)->to_string(this),
arc->fromEdge()->to_string(),
prev_edge->to(graph_)->to_string(this),
arc->toEdge()->to_string());
}
else
prev_str += "NULL";
prev_str = "prev NULL";
}
report_->report(" {} {} {} / {} {}{}", rf->shortName(),
path->minMax(this)->to_string(),
@ -3297,44 +3293,28 @@ Search::seedInvalidRequireds()
class FindEndRequiredVisitor : public PathEndVisitor
{
public:
FindEndRequiredVisitor(RequiredCmp *required_cmp,
FindEndRequiredVisitor(RequiredCmp &required_cmp,
const StaState *sta);
FindEndRequiredVisitor(const StaState *sta);
~FindEndRequiredVisitor() override;
PathEndVisitor *copy() const override;
void visit(PathEnd *path_end) override;
protected:
const StaState *sta_;
RequiredCmp *required_cmp_;
bool own_required_cmp_;
RequiredCmp &required_cmp_;
};
FindEndRequiredVisitor::FindEndRequiredVisitor(RequiredCmp *required_cmp,
FindEndRequiredVisitor::FindEndRequiredVisitor(RequiredCmp &required_cmp,
const StaState *sta) :
sta_(sta),
required_cmp_(required_cmp),
own_required_cmp_(false)
required_cmp_(required_cmp)
{
}
FindEndRequiredVisitor::FindEndRequiredVisitor(const StaState *sta) :
sta_(sta),
required_cmp_(new RequiredCmp),
own_required_cmp_(true)
{
}
FindEndRequiredVisitor::~FindEndRequiredVisitor()
{
if (own_required_cmp_)
delete required_cmp_;
}
PathEndVisitor *
FindEndRequiredVisitor::copy() const
{
return new FindEndRequiredVisitor(sta_);
return new FindEndRequiredVisitor(*this);
}
void
@ -3345,7 +3325,7 @@ FindEndRequiredVisitor::visit(PathEnd *path_end)
const MinMax *min_max = path->minMax(sta_)->opposite();
size_t path_index = path->pathIndex(sta_);
Required required = path_end->requiredTime(sta_);
required_cmp_->requiredSet(path_index, required, min_max, sta_);
required_cmp_.requiredSet(path_index, required, min_max, sta_);
}
}
@ -3355,7 +3335,7 @@ Search::seedRequired(Vertex *vertex)
debugPrint(debug_, "search", 2, "required seed {}",
vertex->to_string(this));
RequiredCmp required_cmp;
FindEndRequiredVisitor seeder(&required_cmp, this);
FindEndRequiredVisitor seeder(required_cmp, this);
required_cmp.requiredsInit(vertex, this);
visit_path_ends_->visitPathEnds(vertex, &seeder);
// Enqueue fanin vertices for back-propagating required times.
@ -3367,7 +3347,7 @@ void
Search::seedRequiredEnqueueFanin(Vertex *vertex)
{
RequiredCmp required_cmp;
FindEndRequiredVisitor seeder(&required_cmp, this);
FindEndRequiredVisitor seeder(required_cmp, this);
required_cmp.requiredsInit(vertex, this);
visit_path_ends_->visitPathEnds(vertex, &seeder);
// Enqueue fanin vertices for back-propagating required times.
@ -3442,31 +3422,25 @@ RequiredCmp::required(size_t path_index)
RequiredVisitor::RequiredVisitor(const StaState *sta) :
PathVisitor(sta),
required_cmp_(new RequiredCmp),
visit_path_ends_(new VisitPathEnds(sta))
{
}
RequiredVisitor::RequiredVisitor(bool make_tag_cache,
const StaState *sta) :
PathVisitor(sta->search()->evalPred(),
make_tag_cache,
sta),
required_cmp_(new RequiredCmp),
visit_path_ends_(new VisitPathEnds(sta))
RequiredVisitor::RequiredVisitor(const RequiredVisitor &required_visitor) :
PathVisitor(required_visitor.search()->evalPred(), true, &required_visitor),
visit_path_ends_(new VisitPathEnds(&required_visitor))
{
}
RequiredVisitor::~RequiredVisitor()
{
delete required_cmp_;
delete visit_path_ends_;
}
VertexVisitor *
RequiredVisitor::copy() const
{
return new RequiredVisitor(true, this);
return new RequiredVisitor(*this);
}
void
@ -3474,7 +3448,7 @@ RequiredVisitor::visit(Vertex *vertex)
{
debugPrint(debug_, "search", 2, "find required {}",
vertex->to_string(this));
required_cmp_->requiredsInit(vertex, this);
required_cmp_.requiredsInit(vertex, this);
// Back propagate requireds from fanout.
visitFanoutPaths(vertex);
// Check for constraints at endpoints that set required times.
@ -3482,7 +3456,7 @@ RequiredVisitor::visit(Vertex *vertex)
FindEndRequiredVisitor seeder(required_cmp_, this);
visit_path_ends_->visitPathEnds(vertex, &seeder);
}
bool changed = required_cmp_->requiredsSave(vertex, this);
bool changed = required_cmp_.requiredsSave(vertex, this);
search_->tnsInvalid(vertex);
if (changed)
@ -3528,8 +3502,8 @@ RequiredVisitor::visitFromToPath(const Pin *,
delayAsString(arc_delay, this),
delayAsString(from_required, this),
min_max == MinMax::max() ? "<" : ">",
delayAsString(required_cmp_->required(path_index), this));
required_cmp_->requiredSet(path_index, from_required, req_min, this);
delayAsString(required_cmp_.required(path_index), this));
required_cmp_.requiredSet(path_index, from_required, req_min, this);
}
else {
if (search_->crprApproxMissingRequireds()) {
@ -3552,8 +3526,8 @@ RequiredVisitor::visitFromToPath(const Pin *,
delayAsString(arc_delay, this),
delayAsString(from_required, this),
min_max == MinMax::max() ? "<" : ">",
delayAsString(required_cmp_->required(path_index), this));
required_cmp_->requiredSet(path_index, from_required, req_min, this);
delayAsString(required_cmp_.required(path_index), this));
required_cmp_.requiredSet(path_index, from_required, req_min, this);
break;
}
}

View File

@ -113,6 +113,7 @@ void
clear_network()
{
Sta *sta = Sta::sta();
sta->clear();
sta->network()->clear();
}
@ -818,8 +819,14 @@ cmd_mode_name()
return Sta::sta()->cmdMode()->name();
}
Mode *
cmd_mode()
{
return Sta::sta()->cmdMode();
}
void
set_mode_cmd(std::string mode_name)
set_cmd_mode(std::string mode_name)
{
Sta::sta()->setCmdMode(mode_name);
}
@ -830,6 +837,12 @@ find_modes(std::string mode_name)
return Sta::sta()->findModes(mode_name);
}
Mode *
find_mode(std::string mode_name)
{
return Sta::sta()->findMode(mode_name);
}
////////////////////////////////////////////////////////////////
CheckErrorSeq &

View File

@ -570,30 +570,46 @@ Sta::clearNonSdc()
Sdc *
Sta::cmdSdc() const
{
return cmdMode()->sdc();
return cmd_mode_->sdc();
}
void
Sta::setCmdMode(std::string_view mode_name)
{
if (!mode_name.empty()) {
if (!mode_name_map_.contains(mode_name)) {
if (modes_.size() == 1 && modes_[0]->name() == "default") {
// No need for default mode if one is defined.
delete modes_[0];
mode_name_map_.clear();
modes_.clear();
Mode *mode = findKey(mode_name_map_, std::string(mode_name));
if (mode) {
// Sync scene with mode. Note that multiple scenes can share a mode.
Scene *mode_scene = nullptr;
for (Scene *scene : scenes_) {
if (scene->mode() == mode) {
if (mode_scene) {
report_->warn(1556, "multiple scenes reference mode {}", mode_name);
break;
}
mode_scene = scene;
}
Mode *mode = new Mode(mode_name, mode_name_map_.size(), this);
mode_name_map_[std::string(mode_name)] = mode;
modes_.push_back(mode);
mode->sim()->setMode(mode);
mode->sim()->setObserver(new StaSimObserver(this));
if (scenes_.size() == 1 && scenes_[0]->name() == "default")
scenes_[0]->setMode(mode);
updateComponentsState();
}
if (mode_scene)
cmd_scene_ = mode_scene;
cmd_mode_ = mode;
}
else {
if (modes_.size() == 1 && modes_[0]->name() == "default") {
// No need for default mode if one is defined.
delete modes_[0];
mode_name_map_.clear();
modes_.clear();
}
Mode *mode = new Mode(mode_name, mode_name_map_.size(), this);
mode_name_map_[std::string(mode_name)] = mode;
modes_.push_back(mode);
mode->sim()->setMode(mode);
mode->sim()->setObserver(new StaSimObserver(this));
cmd_mode_ = mode;
if (scenes_.size() == 1 && scenes_[0]->name() == "default")
scenes_[0]->setMode(mode);
updateComponentsState();
}
}
@ -2190,6 +2206,22 @@ Sta::checkExceptionToPins(ExceptionTo *to,
}
}
void
Sta::writeSdc(std::string_view filename,
std::string_view mode_name,
bool leaf,
bool native,
int digits,
bool gzip,
bool no_timestamp)
{
Mode *mode = findMode(mode_name);
if (mode)
writeSdc(mode->sdc(), filename, leaf, native, digits, gzip, no_timestamp);
else
report_->warn(1561, "mode {} not found.", mode_name);
}
void
Sta::writeSdc(const Sdc *sdc,
std::string_view filename,
@ -2514,6 +2546,7 @@ Sta::makeDefaultScene()
makeScene(name, mode, parasitics);
cmd_scene_ = scenes_[0];
cmd_mode_ = mode;
}
// define_corners (before read_liberty).
@ -2603,8 +2636,8 @@ Sta::makeScene(const std::string &name,
if (scenes_.size() == 1 && findScene("default"))
deleteScenes();
Scene *scene =
new Scene(name, scenes_.size(), mode, parasitics_min, parasitics_max);
Scene *scene = new Scene(name, scenes_.size(), mode,
parasitics_min, parasitics_max);
scene_name_map_[name] = scene;
scenes_.push_back(scene);
mode->addScene(scene);
@ -2696,6 +2729,7 @@ void
Sta::setCmdScene(Scene *scene)
{
cmd_scene_ = scene;
cmd_mode_ = scene->mode();
}
SceneSeq
@ -3270,7 +3304,7 @@ EndpointPathEndVisitor::EndpointPathEndVisitor(std::string_view path_group_name,
PathEndVisitor *
EndpointPathEndVisitor::copy() const
{
return new EndpointPathEndVisitor(path_group_name_, min_max_, sta_);
return new EndpointPathEndVisitor(*this);
}
void
@ -3523,7 +3557,7 @@ Sta::findRequired(Vertex *vertex)
search_->findAllArrivals();
if (search_->isEndpoint(vertex)
// Need to include downstream required times if there is fanout.
&& !hasFanout(vertex, search_->searchAdj(), graph_, cmdMode()))
&& !hasFanout(vertex, search_->searchAdj(), graph_, cmd_mode_))
search_->seedRequired(vertex);
else
search_->findRequireds(vertex->level());
@ -3921,14 +3955,14 @@ Sta::findLogicConstants()
{
ensureGraph();
// Sdc independent constants so any mode should return the same values.
Sim *sim = cmdMode()->sim();
Sim *sim = cmd_mode_->sim();
sim->findLogicConstants();
}
void
Sta::clearLogicConstants()
{
Sim *sim = cmdMode()->sim();
Sim *sim = cmd_mode_->sim();
sim->clear();
}
@ -5062,7 +5096,8 @@ Sta::clockDomains(const Pin *pin,
const Mode *mode)
{
searchPreamble();
search_->findAllArrivals();
Vertex *vertex = graph_->pinLoadVertex(pin);
search_->findArrivals(vertex->level());
return search_->clockDomains(pin, mode);
}

View File

@ -191,53 +191,55 @@ proc set_unit_values { unit key suffix key_var } {
################################################################
define_cmd_args "delete_from_list" {list objs}
define_cmd_args "delete_from_list" {list delete}
proc delete_from_list { list objects } {
delete_objects_from_list_cmd $list $objects
proc delete_from_list { list delete } {
delete_objects_from_list_cmd $list $delete
}
proc delete_objects_from_list_cmd { list objects } {
set list0 [lindex $list 0]
set list_is_object [is_object $list0]
set list_type [object_type $list0]
foreach obj $objects {
# If the list is a collection of tcl objects (returned by get_*),
# convert the obj to be removed from a name to an object of the same
# type.
if {$list_is_object && ![is_object $obj]} {
if {$list_type == "Clock"} {
set obj [find_clock $obj]
} elseif {$list_type == "Port"} {
set top_instance [top_instance]
set top_cell [$top_instance cell]
set obj [$top_cell find_port $obj]
} elseif {$list_type == "Pin"} {
set obj [find_pin $obj]
} elseif {$list_type == "Instance"} {
set obj [find_instance $obj]
} elseif {$list_type == "Net"} {
set obj [find_net $obj]
} elseif {$list_type == "LibertyLibrary"} {
set obj [find_liberty $obj]
} elseif {$list_type == "LibertyCell"} {
set obj [find_liberty_cell $obj]
} elseif {$list_type == "LibertyPort"} {
set obj [get_lib_pins $obj]
} else {
sta_error 164 "unsupported object type $list_type."
proc delete_objects_from_list_cmd { list delete } {
if { $list != {} } {
set list0 [lindex $list 0]
set list_is_objects [is_object $list0]
foreach obj $delete {
# If the list is a collection of tcl objects (returned by get_*),
# convert the obj to be removed from a name to an object of the same
# type.
if {$list_is_objects && ![is_object $obj]} {
set list_type [object_type $list0]
if {$list_type == "Clock"} {
set obj [find_clock $obj]
} elseif {$list_type == "Port"} {
set top_instance [top_instance]
set top_cell [$top_instance cell]
set obj [$top_cell find_port $obj]
} elseif {$list_type == "Pin"} {
set obj [find_pin $obj]
} elseif {$list_type == "Instance"} {
set obj [find_instance $obj]
} elseif {$list_type == "Net"} {
set obj [find_net $obj]
} elseif {$list_type == "LibertyLibrary"} {
set obj [find_liberty $obj]
} elseif {$list_type == "LibertyCell"} {
set obj [find_liberty_cell $obj]
} elseif {$list_type == "LibertyPort"} {
set obj [get_lib_pins $obj]
} else {
sta_error 164 "unsupported object type $list_type."
}
}
set index [lsearch $list $obj]
if { $index != -1 } {
set list [lreplace $list $index $index]
}
}
set index [lsearch $list $obj]
if { $index != -1 } {
set list [lreplace $list $index $index]
}
}
return $list
}
################################################################
proc set_cmd_namespace { namespc } {
if { $namespc == "sdc" || $namespc == "sta" } {
set_cmd_namespace_cmd $namespc
@ -245,7 +247,7 @@ proc set_cmd_namespace { namespc } {
sta_error 165 "unknown namespace $namespc."
}
}
################################################################
define_cmd_args "report_object_full_names" {objects}
@ -295,32 +297,5 @@ proc full_name_cmp { obj1 obj2 } {
return [string compare [get_full_name $obj1] [get_full_name $obj2]]
}
proc get_object_type { obj } {
set object_type [object_type $obj]
if { $object_type == "Clock" } {
return "clock"
} elseif { $object_type == "LibertyCell" } {
return "lib_cell"
} elseif { $object_type == "LibertyPort" } {
return "lib_pin"
} elseif { $object_type == "Cell" } {
return "cell"
} elseif { $object_type == "Instance" } {
return "instance"
} elseif { $object_type == "Port" } {
return "port"
} elseif { $object_type == "Pin" } {
return "pin"
} elseif { $object_type == "Net" } {
return "net"
} elseif { $object_type == "Edge" } {
return "timing_arc"
} elseif { $object_type == "TimingArcSet" } {
return "timing_arc"
} else {
return "?"
}
}
# sta namespace end.
# namespace sta
}

View File

@ -102,7 +102,7 @@ define_cmd_args "set_scene" {scene_name}
proc set_scene { args } {
check_argc_eq1 "set_scene" $args
set_scene_cmd [lindex $args 0]
set_cmd_scene [lindex $args 0]
}
################################################################
@ -141,7 +141,7 @@ define_cmd_args "set_mode" {mode_name}
proc set_mode { args } {
check_argc_eq1 "set_mode" $args
set_mode_cmd [lindex $args 0]
set_cmd_mode [lindex $args 0]
}
################################################################

View File

@ -30,6 +30,9 @@
%{
#include <cctype>
#include <string>
#include "Network.hh"
#include "Liberty.hh"
#include "FuncExpr.hh"
@ -52,7 +55,7 @@ namespace sta {
typedef MinMaxAll MinMaxAllNull;
#if TCL_MAJOR_VERSION < 9
typedef int Tcl_Size;
typedef int Tcl_Size;
#endif
template <class TYPE>
@ -256,14 +259,51 @@ setPtrTclList(SET_TYPE *set,
Tcl_SetObjResult(interp, list);
}
////////////////////////////////////////////////////////////////
} // namespace sta
using namespace sta;
%}
////////////////////////////////////////////////////////////////
%inline %{
inline bool
is_object(const char *obj)
{
// _hexaddress_p_type
const std::string s(obj);
if (s.empty() || s[0] != '_')
return false;
const size_t hex_digits = sizeof(void *) * 2;
if (s.size() < 1 + hex_digits + 3)
return false;
for (size_t i = 1; i < 1 + hex_digits; i++) {
if (!std::isxdigit(static_cast<unsigned char>(s[i])))
return false;
}
if (s.compare(1 + hex_digits, 3, "_p_") != 0)
return false;
for (size_t i = 1 + hex_digits + 3; i < s.size(); i++) {
char ch = s[i];
if (!(std::isalnum(ch) || ch == '_'))
return false;
}
return true;
}
// Assumes is_object is true.
inline const char *
object_type(const char *obj)
{
if (is_object(obj))
return &obj[1 + sizeof(void*) * 2 + 3];
return "";
}
%}
////////////////////////////////////////////////////////////////
//
// SWIG type definitions.

View File

@ -156,11 +156,8 @@ proc expand_tests { argv } {
lappend tests $test
}
}
} elseif { [lsearch [group_tests "all"] $arg] != -1 } {
lappend tests $arg
} else {
puts "Error: test $arg not found."
incr errors(no_cmd)
lappend tests $arg
}
}
return $tests
@ -525,15 +522,11 @@ proc show_summary {} {
if { $valgrind_shared_lib_failure } {
puts "WARNING: valgrind failed because the executable is not statically linked."
}
puts "See $result_dir for log files"
set test_count [llength $tests]
if { [found_errors] } {
if { $errors(error) != 0 } {
puts "Errored $errors(error)/$test_count"
}
if { $errors(fail) != 0 } {
puts "Failed $errors(fail)/$test_count"
}
if { $errors(leak) != 0 } {
puts "Memory leaks in $errors(leak)/$test_count"
}
@ -547,7 +540,7 @@ proc show_summary {} {
puts "No cmd tcl file for $errors(no_cmd)/$test_count"
}
if { $errors(fail) != 0 } {
puts "See $diff_file for differences"
puts "Failed $errors(fail)/$test_count"
}
} else {
puts "Passed $test_count"

View File

@ -224,37 +224,6 @@ set_debug(const char *what,
Sta::sta()->setDebugLevel(what, level);
}
////////////////////////////////////////////////////////////////
bool
is_object(const char *obj)
{
// _hexaddress_p_type
const char *s = obj;
char ch = *s++;
if (ch != '_')
return false;
while (*s && isxdigit(*s))
s++;
if ((s - obj - 1) == sizeof(void*) * 2
&& *s && *s++ == '_'
&& *s && *s++ == 'p'
&& *s && *s++ == '_') {
while (*s && *s != ' ')
s++;
return *s == '\0';
}
else
return false;
}
// Assumes is_object is true.
const char *
object_type(const char *obj)
{
return &obj[1 + sizeof(void*) * 2 + 3];
}
////////////////////////////////////////////////////////////////
//
// Units

View File

@ -447,12 +447,13 @@ VerilogWriter::writeAssigns(const Instance *inst)
if (term) {
Net *net = network_->net(term);
Port *port = network_->port(pin);
if (port
if (net
&& port
&& (include_pwr_gnd_
|| !(network_->isPower(net) || network_->isGround(net)))
&& (network_->direction(port)->isAnyOutput()
|| (include_pwr_gnd_ && network_->direction(port)->isPowerGround()))
&& !stringEqual(network_->name(port), network_->name(net))) {
&& network_->name(port) != network_->name(net)) {
// Port name is different from net name.
std::string port_vname = netVerilogName(std::string(network_->name(port)));
std::string net_vname = netVerilogName(std::string(network_->name(net)));