diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index c6706f90f..973c55dd9 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -201,6 +201,12 @@ public: ParasiticNode *node2) = 0; virtual uint32_t id(const ParasiticCapacitor *capacitor) const = 0; virtual float value(const ParasiticCapacitor *capacitor) const = 0; + // OpenROAD-fork: SI-window -- set a single (coupling) capacitor's value. + // Used by the OpenROAD timing-window aware coupling derate to scale the + // coupling caps of non-overlapping aggressors. Default no-op preserves + // behavior for any Parasitics implementation that does not support it. + virtual void setCapacitorValue(ParasiticCapacitor * /* capacitor */, + float /* value */) {} virtual ParasiticNode *node1(const ParasiticCapacitor *capacitor) const = 0; virtual ParasiticNode *node2(const ParasiticCapacitor *capacitor) const = 0; virtual ParasiticNode *otherNode(const ParasiticCapacitor *capacitor, diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 539c9895a..c3c4e4247 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -72,6 +72,27 @@ using WorstSlacksSeq = std::vector; using DelayDblSeq = std::vector; using ExceptionPathSeq = std::vector; +// OpenROAD-fork: AOCV +// Abstract depth->derate hook for AOCV-style depth-dependent OCV derating +// during forward arrival propagation. OpenSTA core does not depend on the +// concrete dbSta AocvDerateTable; the implementation lives in OpenROAD +// (src/dbSta) and is installed on Search via setAocvDepthDerate(). When no hook +// is installed (the default), timing propagation is byte-identical to upstream. +class AocvDepthDerate +{ + public: + virtual ~AocvDepthDerate() = default; + // depth is the number of combinational data-path stages traversed so far + // (including the arc being derated). + virtual float lateDerate(int depth) const = 0; + virtual float earlyDerate(int depth) const = 0; + // When false for a side, deratedDelayData keeps the flat SDC derate for that + // side instead of overriding it (e.g. a late-only table must not silently + // zero out a user's set_timing_derate -early on the min path). + virtual bool lateActive() const = 0; + virtual bool earlyActive() const = 0; +}; + class Search : public StaState { public: @@ -349,6 +370,80 @@ public: const MinMax *min_max, DcalcAPIndex dcalc_ap, const Sdc *sdc); + // OpenROAD-fork: AOCV + // Data-path arc delay that, when an AocvDepthDerate hook is installed, applies + // a depth-dependent derate (selected by the combinational depth of from_path) + // instead of the flat SDC derate. With no hook installed this forwards + // verbatim to deratedDelay(...) so the result is byte-identical to upstream. + ArcDelay deratedDelayData(const Path *from_path, + const Vertex *from_vertex, + const TimingArc *arc, + const Edge *edge, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + const Sdc *sdc); + // OpenROAD-fork: LVF -- scalar (mean) component of deratedDelayData (flat or + // AOCV-depth derate). Factored out so the LVF synthetic POCV variance can be + // layered on top without altering the mean. + ArcDelay deratedDelayDataMean(const Path *from_path, + const Vertex *from_vertex, + const TimingArc *arc, + const Edge *edge, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + const Sdc *sdc); + // Install (or clear, with nullptr) the AOCV depth-derate hook. Not owned. + void setAocvDepthDerate(const AocvDepthDerate *derate); + const AocvDepthDerate *aocvDepthDerate() const { return aocv_depth_derate_; } + // Combinational depth of the data arrival reaching from_path's vertex, + // including the arc currently being applied (so the first data stage == 1). + int aocvDataDepth(const Path *from_path) const; + + // OpenROAD-fork: POCV + // Parametric on-chip-variation (POCV/LVF) parameters, first slice. These are + // PURELY a default-OFF state holder read by the OpenROAD-side report-only + // command (report_checks_pocv / pocvAdjustPathEnd in src/dbSta). No code on + // the forward-search / arrival-propagation path reads them, so when POCV is + // inactive (the default) timing is byte-identical to upstream. POCV uses + // QUADRATURE (root-sum-square) accumulation of per-stage variation, which does + // not compose with the additive arrival sum the search propagates; hence it is + // intentionally NOT a propagation hook. + struct PocvSigma + { + bool enabled = false; // master flag; false => feature OFF (default) + float per_stage = 0.0f; // fractional per-stage delay sigma (k); 0 => off + float n_sigma = 0.0f; // sign-off sigma multiple (e.g. 3 for 3-sigma) + // OpenROAD-fork: LVF -- when true AND active(), the synthetic per-stage + // variance (k*d_i)^2 is injected into the data-path arc delay's stdDev2 in + // deratedDelayData so the native statistical delay-ops accumulate it in + // quadrature during the forward search (propagation-time POCV). Default + // false => purely the report-only slice => byte-identical baseline timing. + bool propagate = false; + // OpenROAD-fork: LVF-lib -- library-driven POCV. When true the per-stage + // sigma comes from the real Liberty LVF ocv_sigma_* tables via the NATIVE + // delay calc (GateTableModel::gateDelayPocv under PocvMode::normal), NOT + // from the synthetic global per_stage. In this mode the synthetic variance + // injection in deratedDelayData is suppressed so it cannot overwrite the + // library-derived stdDev. Default false => byte-identical baseline. + bool from_liberty = false; + // Active only when explicitly enabled with non-zero coefficients. With this + // false the POCV slack equals the flat slack exactly (baseline). Unchanged: + // this governs only the global synthetic-sigma model (per_stage based). + bool active() const { return enabled && per_stage > 0.0f && n_sigma > 0.0f; } + // OpenROAD-fork: LVF-lib -- library-driven mode gate. Independent of the + // synthetic per_stage; needs only enabled + a sign-off quantile (n_sigma). + bool libertyActive() const { return enabled && from_liberty && n_sigma > 0.0f; } + // OpenROAD-fork: LVF -- propagation-time SYNTHETIC injection gate. Fires + // only for the global-sigma model; in library-driven mode the variance + // comes from the native LVF delay calc, so the synthetic injection is + // suppressed (from_liberty => false) to avoid overwriting the library stdDev. + bool propagateActive() const + { + return propagate && active() && !from_liberty; // OpenROAD-fork: LVF-lib + } + }; + const PocvSigma &pocvSigma() const { return pocv_sigma_; } + void setPocvSigma(const PocvSigma &sigma) { pocv_sigma_ = sigma; } TagGroup *tagGroup(const Vertex *vertex) const; TagGroup *tagGroup(TagGroupIndex index) const; @@ -670,6 +765,17 @@ protected: VisitPathEnds *visit_path_ends_; GatedClk *gated_clk_; CheckCrpr *check_crpr_; + + // OpenROAD-fork: AOCV + // Optional depth-dependent OCV derate hook. nullptr (the default) means the + // feature is OFF and propagation is byte-identical to upstream. Not owned. + const AocvDepthDerate *aocv_depth_derate_{nullptr}; + + // OpenROAD-fork: POCV + // Parametric OCV (POCV/LVF) parameters. Default-constructed => inactive, so + // the feature is OFF and nothing on the propagation path reads it. Only the + // OpenROAD-side report-only command consumes this (see pocvSigma()). + PocvSigma pocv_sigma_; }; // Eval across latch D->Q edges. diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 6ab2bcbb0..6efc7c531 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -1414,6 +1414,18 @@ ConcreteParasitics::value(const ParasiticCapacitor *capacitor) const return ccapacitor->value(); } +// OpenROAD-fork: SI-window +void +ConcreteParasitics::setCapacitorValue(ParasiticCapacitor *capacitor, + float value) +{ + if (capacitor == nullptr) + return; + ConcreteParasiticCapacitor *ccapacitor = + static_cast(capacitor); + ccapacitor->setValue(value); +} + ParasiticNode * ConcreteParasitics::node1(const ParasiticCapacitor *capacitor) const { diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 936609c17..c75465f16 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -170,6 +170,8 @@ public: ParasiticNode *node2) override; uint32_t id(const ParasiticCapacitor *capacitor) const override; float value(const ParasiticCapacitor *capacitor) const override; + // OpenROAD-fork: SI-window + void setCapacitorValue(ParasiticCapacitor *capacitor, float value) override; ParasiticNode *node1(const ParasiticCapacitor *capacitor) const override; ParasiticNode *node2(const ParasiticCapacitor *capacitor) const override; diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index 8b36ff4ab..7121a5644 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -303,6 +303,9 @@ public: ConcreteParasiticNode *node2); uint32_t id() const { return id_; } float value() const { return value_; } + // OpenROAD-fork: SI-window -- per-device value override used by the + // OpenROAD window-aware coupling derate. Not used by stock OpenSTA. + void setValue(float value) { value_ = value; } ConcreteParasiticNode *node1() const { return node1_; } ConcreteParasiticNode *node2() const { return node2_; } void replaceNode(ConcreteParasiticNode *from_node, diff --git a/search/Search.cc b/search/Search.cc index 879c12476..720a78404 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -25,6 +25,7 @@ #include "Search.hh" #include +#include #include #include @@ -2216,8 +2217,10 @@ PathVisitor::visitFromPath(const Pin *from_pin, else { if (!(sdc->isPathDelayInternalFromBreak(to_pin) || sdc->isPathDelayInternalToBreak(from_pin))) { - arc_delay = search_->deratedDelay(from_vertex, arc, edge, false, - min_max, dcalc_ap, sdc); + // OpenROAD-fork: AOCV -- depth-dependent derate when a hook is installed; + // byte-identical to deratedDelay(... false ...) when it is not. + arc_delay = search_->deratedDelayData(from_path, from_vertex, arc, edge, + min_max, dcalc_ap, sdc); if (!delayInf(arc_delay, this)) { to_arrival = delaySum(from_arrival, arc_delay, this); to_tag = search_->thruTag(from_tag, edge, to_rf, tag_cache_); @@ -3024,6 +3027,109 @@ Search::deratedDelay(const Vertex *from_vertex, return delayProduct(delay, derate, this);; } +// OpenROAD-fork: AOCV +void +Search::setAocvDepthDerate(const AocvDepthDerate *derate) +{ + aocv_depth_derate_ = derate; +} + +// OpenROAD-fork: AOCV +// Count combinational data-path stages on from_path's prev chain, plus 1 for +// the arc currently being applied. The prev chain is complete here because the +// forward BFS commits prev-path pointers in topological level order before a +// vertex's own paths are extended. +int +Search::aocvDataDepth(const Path *from_path) const +{ + int depth = 1; // the arc about to be derated is the next stage + const Path *p = from_path; + while (p) { + const TimingArc *arc = p->prevArc(this); + if (arc && arc->role() == TimingRole::combinational()) + depth++; + p = p->prevPath(); + } + return depth; +} + +// OpenROAD-fork: AOCV +ArcDelay +Search::deratedDelayData(const Path *from_path, + const Vertex *from_vertex, + const TimingArc *arc, + const Edge *edge, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + const Sdc *sdc) +{ + // First compute the scalar (flat or AOCV-depth) data-path delay. This block + // is byte-identical to the AOCV slice; nothing here changes the mean delay. + ArcDelay delay = deratedDelayDataMean(from_path, from_vertex, arc, edge, + min_max, dcalc_ap, sdc); + + // OpenROAD-fork: LVF -- propagation-time POCV variance injection. + // When the synthetic per-stage sigma model is in -propagate mode, attach a + // statistical variance (k*d_i)^2 to this stage's delay so the native + // DelayOpsNormal accumulates it in quadrature through delaySum during the + // forward search. The mean is left untouched (above), so with the feature + // off this is a no-op and timing is byte-identical. Only combinational cell + // arcs carry per-stage variation (matches the report-only slice; clock / + // CRPR / check arcs keep their flat scalar treatment). + if (pocv_sigma_.propagateActive() + && edge->role() == TimingRole::combinational()) { + const float k = pocv_sigma_.per_stage; + const float d = delayAsFloat(delay); // nominal stage delay mean + if (d > 0.0f && std::isfinite(d)) { + const float stage_sigma = k * d; // per-stage sigma = k * d_i + // makeDelay(mean, std_dev) stores std_dev^2 == (k*d_i)^2 as the variance, + // which is exactly the per-stage contribution the path accumulates in + // quadrature. The mean is preserved so the nominal arrival is unchanged. + // Any prior arc variance (e.g. from LVF liberty) is replaced by the + // synthetic model on purpose -- they are mutually exclusive configs. + delay = makeDelay(d, stage_sigma); + } + } + return delay; +} + +// OpenROAD-fork: AOCV -- scalar (mean) data-path delay: flat SDC derate, or the +// AOCV depth-derate when that hook is installed. Factored out of +// deratedDelayData so the LVF variance injection can layer on top without +// changing the mean. Byte-identical to the original AOCV body. +ArcDelay +Search::deratedDelayDataMean(const Path *from_path, + const Vertex *from_vertex, + const TimingArc *arc, + const Edge *edge, + const MinMax *min_max, + DcalcAPIndex dcalc_ap, + const Sdc *sdc) +{ + // Feature OFF: identical to the original data-path derate call. + if (aocv_depth_derate_ == nullptr) + return deratedDelay(from_vertex, arc, edge, false, min_max, dcalc_ap, sdc); + + // Only depth-derate combinational cell arcs; everything else keeps the flat + // SDC derate (matches the report-only slice and avoids perturbing clock / + // CRPR / check arcs). + if (edge->role() != TimingRole::combinational()) + return deratedDelay(from_vertex, arc, edge, false, min_max, dcalc_ap, sdc); + + // If the table has no entries for this side (late/early), keep the flat SDC + // derate for that side rather than silently overriding it with 1.0. + const bool is_late = (min_max == MinMax::max()); + if (is_late ? !aocv_depth_derate_->lateActive() + : !aocv_depth_derate_->earlyActive()) + return deratedDelay(from_vertex, arc, edge, false, min_max, dcalc_ap, sdc); + + const int depth = aocvDataDepth(from_path); + const float derate = is_late ? aocv_depth_derate_->lateDerate(depth) + : aocv_depth_derate_->earlyDerate(depth); + const ArcDelay &delay = graph_->arcDelay(edge, arc, dcalc_ap); + return delayProduct(delay, derate, this); +} + float Search::timingDerate(const Vertex *from_vertex, const TimingArc *arc,