From c1d75aa18ccf57f9b7775013b3cd112be8893998 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Sun, 28 Jun 2026 04:45:43 +0000 Subject: [PATCH 1/5] OpenROAD-fork: ccs-delay -- read-only ReceiverModel capacitance accessors Add hasCapacitanceModel()/capacitance() read-only accessors on ReceiverModel to evaluate the CCS receiver-capacitance tables that OpenSTA already parses into capacitance_models_ but never consumes. Used only by the additive OpenROAD report_ccs_receiver_delta diagnostic; the NLDM delay path does not call them, so flag-off behavior is byte-identical to baseline. Signed-off-by: Saurav Singh --- include/sta/TableModel.hh | 18 +++++++++++++++++ liberty/TableModel.cc | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/include/sta/TableModel.hh b/include/sta/TableModel.hh index 40855a75c..7ab1bedfb 100644 --- a/include/sta/TableModel.hh +++ b/include/sta/TableModel.hh @@ -443,7 +443,25 @@ public: const RiseFall *rf); static bool checkAxes(const TableModel *table); + // OpenROAD-fork: ccs-delay -- read-only accessors so the OpenROAD (dbSta) + // layer can SURFACE the CCS receiver-capacitance that OpenSTA already + // parses but never evaluates. These do NOT participate in delay calc; the + // NLDM delay path is unchanged. See src/dbSta/src/CcsReceiverReport.cc. + // + // hasCapacitanceModel: true if segment/rf has a parsed receiver-cap table. + // capacitance: interpolated receiver capacitance (library cap units) at the + // given input transition (in_slew) and, for 2D tables, output net cap + // (out_cap). segment 0 = receiver_capacitance1 (Cr1), 1 = Cr2. + bool hasCapacitanceModel(size_t segment, const RiseFall *rf) const; + float capacitance(size_t segment, + const RiseFall *rf, + float in_slew, + float out_cap) const; + private: + const TableModel *capacitanceModel(size_t segment, + const RiseFall *rf) const; + std::vector capacitance_models_; }; diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 5cdd92f2e..6a584a579 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -446,6 +446,48 @@ ReceiverModel::checkAxes(const TableModel *table) && axis3 == nullptr); } +// OpenROAD-fork: ccs-delay -- read-only accessors. These evaluate the CCS +// receiver-capacitance tables that OpenSTA already parses into +// capacitance_models_ but never consumes. They are used only by the additive +// OpenROAD report (report_ccs_receiver_delta); the NLDM delay path does not +// call them, so default/flag-off behavior is byte-identical to baseline. +const TableModel * +ReceiverModel::capacitanceModel(size_t segment, + const RiseFall *rf) const +{ + const size_t idx = segment * RiseFall::index_count + rf->index(); + if (idx >= capacitance_models_.size()) + return nullptr; + const TableModel &model = capacitance_models_[idx]; + // Empty (default-constructed) slots have no backing table. + if (model.table() == nullptr) + return nullptr; + return &model; +} + +bool +ReceiverModel::hasCapacitanceModel(size_t segment, + const RiseFall *rf) const +{ + return capacitanceModel(segment, rf) != nullptr; +} + +float +ReceiverModel::capacitance(size_t segment, + const RiseFall *rf, + float in_slew, + float out_cap) const +{ + const TableModel *model = capacitanceModel(segment, rf); + if (model == nullptr) + return 0.0f; + // ReceiverModel::checkAxes guarantees axis1 is input_net_transition (1D) or + // input_net_transition x total_output_net_capacitance (2D, canonicalized). + // Table::findValue ignores trailing args for lower-order tables, so passing + // (in_slew, out_cap, 0) is correct for both 1D and 2D receiver-cap tables. + return model->findValue(in_slew, out_cap, 0.0f); +} + //////////////////////////////////////////////////////////////// CheckTableModel::CheckTableModel(LibertyCell *cell, From 2c6ffa1e3c7a390c928c66750d3634debfd2e204 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Sun, 28 Jun 2026 06:55:32 +0000 Subject: [PATCH 2/5] ccs-delay2: expose converged CCS effective capacitance (read-only) Additive read-only accessor effectiveCapacitance() on ArcDelayCalc (base returns a negative sentinel) overridden by CcsCeffDelayCalc to return the effective capacitance (region 0) that the most recent gateDelay() converged to, or a negative sentinel when gateDelay() fell back to the NLDM table calc. Used by OpenROAD's report_ccs_driver_delta (dbSta) diagnostic to surface the driver-side Ceff the CCS calculator already computes internally but did not expose. Purely additive: no existing delay value or code path changes, so timing is byte-identical when the accessor is not called. OpenROAD-fork: ccs-delay2 Signed-off-by: Saurav Singh --- dcalc/CcsCeffDelayCalc.cc | 3 +++ dcalc/CcsCeffDelayCalc.hh | 11 +++++++++++ include/sta/ArcDelayCalc.hh | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 9de98d81f..efe421f46 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -94,6 +94,7 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, parasitics_ = scene->parasitics(min_max); parasitic_ = parasitic; output_waveforms_ = nullptr; + last_ceff_ = -1.0; // OpenROAD-fork: ccs-delay2 -- reset; set on CCS path. const GateTableModel *table_model = arc->gateTableModel(scene, min_max); if (table_model && parasitic) { @@ -126,6 +127,8 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, double gate_delay, drvr_slew; gateDelaySlew(drvr_library, gate_delay, drvr_slew); + // OpenROAD-fork: ccs-delay2 -- expose the converged effective cap. + last_ceff_ = region_ceff_[0]; debugPrint(debug_, "ccs_dcalc", 2, "gate_delay {} drvr_slew {}", delayAsString(gate_delay, this), delayAsString(drvr_slew, this)); diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 90f275616..567fe223f 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -62,6 +62,14 @@ public: const MinMax *min_max, int digits) override; + // OpenROAD-fork: ccs-delay2 -- read-only accessor exposing the effective + // capacitance (region 0) that the most recent successful CCS gateDelay() + // converged to. Returns a negative value if the last gateDelay() fell back + // to the NLDM table calculator (no CCS waveforms / out-of-bounds / no + // pi-model parasitic). Purely additive: does not affect any delay value or + // the active delay path. + double effectiveCapacitance() const override { return last_ceff_; } + // Record waveform for drvr/load pin. void watchPin(const Pin *pin) override; void clearWatchPins() override; @@ -148,6 +156,9 @@ protected: Region region_ramp_times_; Region region_ramp_slopes_; bool vl_fail_{false}; + // OpenROAD-fork: ccs-delay2 -- last converged effective cap (region 0), or + // a negative sentinel when the previous gateDelay() used the NLDM fallback. + double last_ceff_{-1.0}; // Waveform recording. WatchPinValuesMap watch_pin_values_; diff --git a/include/sta/ArcDelayCalc.hh b/include/sta/ArcDelayCalc.hh index eacad4454..8e92b7678 100644 --- a/include/sta/ArcDelayCalc.hh +++ b/include/sta/ArcDelayCalc.hh @@ -259,6 +259,12 @@ public: const MinMax *min_max, int digits) = 0; virtual void finishDrvrPin() = 0; + + // OpenROAD-fork: ccs-delay2 -- effective capacitance the most recent + // gateDelay() converged to. Meaningful only for CCS Ceff calculators; the + // base returns a negative sentinel (no Ceff / not a Ceff-solving calc). + // Read-only; does not affect any delay value or the active delay path. + virtual double effectiveCapacitance() const { return -1.0; } }; } // namespace sta From 19dd7c2a719c866c489a2ef61bde2f176d98d143 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Sun, 28 Jun 2026 12:17:40 +0000 Subject: [PATCH 3/5] ccs-delay-path: fix CcsCeffDelayCalc::reportGateDelay crash (Signal 11) reportGateDelay() can be called directly (e.g. by report_dcalc) without a preceding gateDelay() for the same arc. It used the stale drvr_pin_ member and an unset parasitics_ member, dereferencing bad pointers and crashing. Resolve parasitics_ from the scene and use the drvr_pin parameter, matching gateDelay(). Marked OpenROAD-fork: ccs-delay-path. Signed-off-by: Saurav Singh --- dcalc/CcsCeffDelayCalc.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index efe421f46..48804acc0 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -668,14 +668,20 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, { Parasitic *pi_elmore = nullptr; const RiseFall *rf = arc->toEdge()->asRiseFall(); + // OpenROAD-fork: ccs-delay-path -- reportGateDelay() can be invoked directly + // (e.g. by report_dcalc) without a preceding gateDelay() call, so the + // parasitics_ member is unset/stale here. Resolve it from the scene like + // gateDelay() does, otherwise the parasitics_ deref below crashes (Signal 11). + parasitics_ = scene->parasitics(min_max); if (parasitic && !parasitics_->isPiElmore(parasitic)) { + // Use the drvr_pin PARAMETER, not the stale drvr_pin_ member. pi_elmore = - parasitics_->reduceToPiElmore(parasitic, drvr_pin_, rf, scene, min_max); + parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf, scene, min_max); } std::string report = table_dcalc_->reportGateDelay(drvr_pin, arc, in_slew, load_cap, pi_elmore, load_pin_index_map, scene, min_max, digits); - parasitics_->deleteDrvrReducedParasitics(drvr_pin); + parasitics_->deleteDrvrReducedParasitics(drvr_pin); // OpenROAD-fork: ccs-delay-path return report; } From 07d2dc1e7d2eb563325ca8d66f831bb3481ad018 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Mon, 29 Jun 2026 09:58:33 +0000 Subject: [PATCH 4/5] ccs-receiver: use CCS receiver_capacitance in ccs_ceff Ceff solve (flag-gated) Flag-gated (default OFF) addition to CcsCeffDelayCalc: when the receiver model is enabled, the per-region far-cap (c1) contribution of any load pin that carries a CCS receiver_capacitance model is swapped from its constant NLDM pin cap to the library's region-dependent receiver cap (Cr1 in the Miller/transition region below the receiver threshold, Cr2 in the settled region). Pins without a receiver model keep the constant NLDM contribution (graceful fallback). Default OFF -> byte-identical to prior ccs_ceff. - CcsCeffDelayCalc::setReceiverModelEnabled/receiverModelEnabled static toggle - initReceiverModel() gathers per-pin Cr1/Cr2/NLDM caps before the solve - regionFarCap() returns c1_ unchanged when off; region cap when on - DelayCalc.i: set_ccs_receiver_model_enabled / ccs_receiver_model_enabled All src/sta edits marked // OpenROAD-fork: ccs-receiver. Signed-off-by: Saurav Singh --- dcalc/CcsCeffDelayCalc.cc | 128 ++++++++++++++++++++++++++++++++++++-- dcalc/CcsCeffDelayCalc.hh | 34 ++++++++++ dcalc/DelayCalc.i | 19 ++++++ 3 files changed, 177 insertions(+), 4 deletions(-) diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index 48804acc0..d58a2f2ce 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -36,11 +36,29 @@ #include "Network.hh" #include "Parasitics.hh" #include "Scene.hh" +#include "TableModel.hh" #include "TimingArc.hh" #include "Units.hh" namespace sta { +// OpenROAD-fork: ccs-receiver -- process-global toggle (see header). Default +// OFF: ccs_ceff is byte-identical to the constant-Cp behavior until the +// OpenROAD set_ccs_delay_calc -receiver_model flag flips it on. +static bool ccs_receiver_model_enabled = false; + +void +CcsCeffDelayCalc::setReceiverModelEnabled(bool enabled) +{ + ccs_receiver_model_enabled = enabled; +} + +bool +CcsCeffDelayCalc::receiverModelEnabled() +{ + return ccs_receiver_model_enabled; +} + // Implementaion based on: // "Gate Delay Estimation with Library Compatible Current Source Models // and Effective Capacitance", D. Garyfallou et al, @@ -126,6 +144,9 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, drvr_rf_->shortName()); double gate_delay, drvr_slew; + // OpenROAD-fork: ccs-receiver -- gather region-dependent receiver caps + // before the Ceff solve. No-op when the flag is off. + initReceiverModel(load_pin_index_map, scene, min_max); gateDelaySlew(drvr_library, gate_delay, drvr_slew); // OpenROAD-fork: ccs-delay2 -- expose the converged effective cap. last_ceff_ = region_ceff_[0]; @@ -159,6 +180,100 @@ CcsCeffDelayCalc::gateDelay(const Pin *drvr_pin, load_pin_index_map, scene, min_max); } +// OpenROAD-fork: ccs-receiver -- gather the region-dependent receiver caps for +// the load pins. The CCS receiver_capacitance model lives on the load pin's +// GateTableModel (keyed by the load pin's input transition rf), the same place +// the additive report (CcsReceiverReport.cc) reads it. We sum, over every load +// pin that carries a receiver model: +// - its NLDM static pin cap (already folded into the pi-model far cap c1_), +// - segment 0 (Cr1, active/Miller region) receiver cap, +// - segment 1 (Cr2, settled region) receiver cap, +// evaluated at the driver output transition slew (== the load pin input slew) +// and the stage load cap (only used by 2D receiver-cap tables). Pins without a +// receiver model are left out so they keep their constant NLDM contribution. +// When the flag is off this clears the accumulators (no behavior change). +void +CcsCeffDelayCalc::initReceiverModel(const LoadPinIndexMap &load_pin_index_map, + const Scene *scene, + const MinMax *min_max) +{ + recv_has_model_ = false; + recv_nldm_cap_ = 0.0; + recv_cr1_cap_ = 0.0; + recv_cr2_cap_ = 0.0; + if (!ccs_receiver_model_enabled) + return; + + const float drvr_slew_f = static_cast(in_slew_); + for (const auto &[load_pin, load_idx] : load_pin_index_map) { + const LibertyPort *load_port = network_->libertyPort(load_pin); + if (load_port == nullptr) + continue; + const LibertyCell *load_cell = load_port->libertyCell(); + if (load_cell == nullptr) + continue; + // The receiver cap is indexed by the load pin's input transition, which + // equals the driver output transition for this stage. + const RiseFall *rf = drvr_rf_; + const ReceiverModel *receiver = nullptr; + const TimingArcSetSeq &arc_sets = load_cell->timingArcSetsFrom(load_port); + for (TimingArcSet *arc_set : arc_sets) { + TimingModel *model = arc_set->model(rf); + auto *gate_model = dynamic_cast(model); + if (gate_model == nullptr) + continue; + const ReceiverModel *rm = gate_model->receiverModel(); + if (rm && rm->hasCapacitanceModel(0, rf)) { + receiver = rm; + break; + } + } + if (receiver == nullptr) + // No receiver model on this pin: leave it folded into c1_ (fallback). + continue; + + const float nldm_cap = load_port->capacitance(rf, min_max); + const float cr1 = receiver->capacitance(0, rf, drvr_slew_f, load_cap_); + // Segment 1 (Cr2) is optional; fall back to Cr1 when absent. + const float cr2 = receiver->hasCapacitanceModel(1, rf) + ? receiver->capacitance(1, rf, drvr_slew_f, load_cap_) + : cr1; + recv_has_model_ = true; + recv_nldm_cap_ += nldm_cap; + recv_cr1_cap_ += cr1; + recv_cr2_cap_ += cr2; + debugPrint(debug_, "ccs_dcalc", 2, + "receiver model {} nldm {} cr1 {} cr2 {}", + network_->pathName(load_pin), + capacitance_unit_->asString(nldm_cap), + capacitance_unit_->asString(cr1), + capacitance_unit_->asString(cr2)); + } + (void) scene; +} + +// OpenROAD-fork: ccs-receiver -- far-cap (c1) value used for region/segment +// seg_idx. With the receiver model off (or no contributing pin) this returns +// the unmodified pi-model far cap c1_, so the charge integral is byte-identical +// to the constant-Cp solve. With it on, the receiver pins' constant NLDM cap is +// removed from c1_ and replaced with the region-appropriate value: Cr1 for +// segments below the receiver threshold (region_vth_idx_, the Miller/transition +// region) and Cr2 at/above it (settled region). +double +CcsCeffDelayCalc::regionFarCap(size_t seg_idx) const +{ + if (!ccs_receiver_model_enabled || !recv_has_model_) + return c1_; + const double recv_region_cap = + (seg_idx < region_vth_idx_) ? recv_cr1_cap_ : recv_cr2_cap_; + double c1 = c1_ - recv_nldm_cap_ + recv_region_cap; + // Guard against a pathological library where removing the NLDM cap would + // drive the far cap non-positive; keep the constant behavior in that case. + if (c1 <= 0.0) + return c1_; + return c1; +} + void CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, // Return values. @@ -196,11 +311,16 @@ CcsCeffDelayCalc::gateDelaySlew(const LibertyLibrary *drvr_library, // Note that eqn 8 in the ref'd paper does not properly account // for the charge on c1 from previous segments so it does not // work well. + // OpenROAD-fork: ccs-receiver -- regionFarCap() returns c1_ unchanged + // when the receiver model is off, so this path is byte-identical by + // default; when on, c1 is the region-appropriate receiver-cap-refined + // far cap (Cr1 in the Miller region, Cr2 once settled). + const double c1_region = regionFarCap(i); double c1_v1, c1_v2, ignore; - vLoad(t1, rpi_ * c1_, c1_v1, ignore); - vLoad(t2, rpi_ * c1_, c1_v2, ignore); - double q1 = seg_v1 * c2_ + c1_v1 * c1_; - double q2 = seg_v2 * c2_ + c1_v2 * c1_; + vLoad(t1, rpi_ * c1_region, c1_v1, ignore); + vLoad(t2, rpi_ * c1_region, c1_v2, ignore); + double q1 = seg_v1 * c2_ + c1_v1 * c1_region; + double q2 = seg_v2 * c2_ + c1_v2 * c1_region; double ceff = (q2 - q1) / (seg_v2 - seg_v1); debugPrint(debug_, "ccs_dcalc", 2, "ceff {}", diff --git a/dcalc/CcsCeffDelayCalc.hh b/dcalc/CcsCeffDelayCalc.hh index 567fe223f..3e5a097e4 100644 --- a/dcalc/CcsCeffDelayCalc.hh +++ b/dcalc/CcsCeffDelayCalc.hh @@ -70,6 +70,20 @@ public: // the active delay path. double effectiveCapacitance() const override { return last_ceff_; } + // OpenROAD-fork: ccs-receiver -- process-global toggle for the CCS + // receiver-capacitance model in the Ceff/charge solve. Default OFF so the + // ccs_ceff engine is byte-identical to the constant-Cp behavior. When ON, + // the per-region far-cap contribution of any load pin that carries a CCS + // receiver_capacitance model is swapped from its constant NLDM pin cap to + // the library's region-dependent receiver cap (Cr1 in the active/Miller + // region below the receiver threshold, Cr2 in the settled region). Pins + // without a receiver model keep their constant NLDM contribution. The flag + // is static because the active ArcDelayCalc is selected by name and copied + // per-thread; a process-global toggle keeps every copy consistent without + // threading new state through the copy ctor. + static void setReceiverModelEnabled(bool enabled); + static bool receiverModelEnabled(); + // Record waveform for drvr/load pin. void watchPin(const Pin *pin) override; void clearWatchPins() override; @@ -159,6 +173,26 @@ protected: // OpenROAD-fork: ccs-delay2 -- last converged effective cap (region 0), or // a negative sentinel when the previous gateDelay() used the NLDM fallback. double last_ceff_{-1.0}; + // OpenROAD-fork: ccs-receiver -- per-solve receiver-capacitance contributions + // summed over the load pins that carry a CCS receiver_capacitance model. + // recv_has_model_ is true when at least one load pin contributed; when false + // the Ceff solve falls back to the constant-Cp behavior (byte-identical). + // recv_nldm_cap_ : sum of the NLDM static pin caps of those pins (the + // quantity already folded into c1_, to be removed). + // recv_cr1_cap_ : sum of segment-0 (Cr1) receiver caps (active/Miller). + // recv_cr2_cap_ : sum of segment-1 (Cr2) receiver caps (settled region). + // These are only consulted when receiverModelEnabled() is true. + bool recv_has_model_{false}; + double recv_nldm_cap_{0.0}; + double recv_cr1_cap_{0.0}; + double recv_cr2_cap_{0.0}; + // Compute the per-segment far-cap (c1) value for region/segment seg_idx, + // applying the receiver model when enabled; otherwise returns c1_. + double regionFarCap(size_t seg_idx) const; + // Populate recv_* from the load pins; no-op (clears) when the flag is off. + void initReceiverModel(const LoadPinIndexMap &load_pin_index_map, + const Scene *scene, + const MinMax *min_max); // Waveform recording. WatchPinValuesMap watch_pin_values_; diff --git a/dcalc/DelayCalc.i b/dcalc/DelayCalc.i index 69e23059a..5467ee379 100644 --- a/dcalc/DelayCalc.i +++ b/dcalc/DelayCalc.i @@ -30,6 +30,7 @@ #include "DelayCalc.hh" #include "Sta.hh" #include "dcalc/ArcDcalcWaveforms.hh" +#include "dcalc/CcsCeffDelayCalc.hh" #include "dcalc/PrimaDelayCalc.hh" %} @@ -54,6 +55,24 @@ set_delay_calculator_cmd(const char *alg) sta::Sta::sta()->setArcDelayCalc(alg); } +// OpenROAD-fork: ccs-receiver -- toggle the CCS receiver-capacitance model in +// the ccs_ceff Ceff solve. Process-global; default OFF (byte-identical to the +// constant-Cp behavior). Selecting/deselecting the ccs_ceff engine is done +// separately via set_delay_calculator_cmd; this only flips the receiver-model +// sub-behavior. delaysInvalid() so the next query re-solves with the new mode. +void +set_ccs_receiver_model_enabled(bool enabled) +{ + sta::CcsCeffDelayCalc::setReceiverModelEnabled(enabled); + sta::Sta::sta()->delaysInvalid(); +} + +bool +ccs_receiver_model_enabled() +{ + return sta::CcsCeffDelayCalc::receiverModelEnabled(); +} + void set_delay_calc_incremental_tolerance(float tol) { From e6e286c81c646e2061141ef947ab9acd75e8d167 Mon Sep 17 00:00:00 2001 From: Saurav Singh Date: Thu, 9 Jul 2026 23:06:19 +0000 Subject: [PATCH 5/5] dcalc: defensive null guards (address review) - reportGateDelay: guard against scene->parasitics(min_max) returning null (parasitics not loaded for the corner) before dereferencing. - ReceiverModel::capacitanceModel: return nullptr on a null RiseFall instead of dereferencing it. Signed-off-by: Saurav Singh --- dcalc/CcsCeffDelayCalc.cc | 5 +++-- liberty/TableModel.cc | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dcalc/CcsCeffDelayCalc.cc b/dcalc/CcsCeffDelayCalc.cc index d58a2f2ce..39eb5c615 100644 --- a/dcalc/CcsCeffDelayCalc.cc +++ b/dcalc/CcsCeffDelayCalc.cc @@ -793,7 +793,7 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, // parasitics_ member is unset/stale here. Resolve it from the scene like // gateDelay() does, otherwise the parasitics_ deref below crashes (Signal 11). parasitics_ = scene->parasitics(min_max); - if (parasitic && !parasitics_->isPiElmore(parasitic)) { + if (parasitics_ && parasitic && !parasitics_->isPiElmore(parasitic)) { // Use the drvr_pin PARAMETER, not the stale drvr_pin_ member. pi_elmore = parasitics_->reduceToPiElmore(parasitic, drvr_pin, rf, scene, min_max); @@ -801,7 +801,8 @@ CcsCeffDelayCalc::reportGateDelay(const Pin *drvr_pin, 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); // OpenROAD-fork: ccs-delay-path + if (parasitics_) + parasitics_->deleteDrvrReducedParasitics(drvr_pin); // OpenROAD-fork: ccs-delay-path return report; } diff --git a/liberty/TableModel.cc b/liberty/TableModel.cc index 6a584a579..327ed0109 100644 --- a/liberty/TableModel.cc +++ b/liberty/TableModel.cc @@ -455,6 +455,8 @@ const TableModel * ReceiverModel::capacitanceModel(size_t segment, const RiseFall *rf) const { + if (rf == nullptr) + return nullptr; const size_t idx = segment * RiseFall::index_count + rf->index(); if (idx >= capacitance_models_.size()) return nullptr;