From 902a1bff86e0f725f47e49457dc260773fa418b6 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 8 Feb 2024 13:54:52 -0700 Subject: [PATCH] parasitics api update commit 5eb41d9304fe43d22dcf32b5346a6c9705c0d0b3 Author: James Cherry Date: Thu Feb 8 11:49:16 2024 -0700 tcl endpoint_count Signed-off-by: James Cherry commit ffb0e0a083edbbdc3753b829641ba26730d3d882 Author: James Cherry Date: Thu Feb 8 10:51:36 2024 -0700 ArcDelayCalc::reduceParasitic Signed-off-by: James Cherry commit ed167b218ed026b0b7427301ace67c3d22cc969a Author: James Cherry Date: Wed Feb 7 22:46:40 2024 -0700 parasitics makeResistor/capacitor rm network arg Signed-off-by: James Cherry commit 41244abfcfdee20ddc9aa8ac80cac2e3e7f68146 Author: James Cherry Date: Wed Feb 7 17:08:04 2024 -0700 arnoldi coupling caps Signed-off-by: James Cherry commit a14d6880be0dc22bf008cae63ec93880c8347ccf Author: James Cherry Date: Wed Feb 7 07:28:31 2024 -0700 parasiticLoad Signed-off-by: James Cherry commit 1cacbd7da71c7f8c5ac311caabd03bb74b66e675 Author: James Cherry Date: Wed Feb 7 07:21:49 2024 -0700 parasitic resistor/capacitor index -> id Signed-off-by: James Cherry commit 6c749158cc94e5a91376721a8ccb71a8a4d020d5 Author: James Cherry Date: Tue Feb 6 21:42:03 2024 -0700 arnoldi Signed-off-by: James Cherry commit 4ffa6002224d76321287f64448929e5ef0ec6edd Author: James Cherry Date: Tue Feb 6 18:27:33 2024 -0700 arnoldi parasitic leak Signed-off-by: James Cherry commit a9666dd7c44126b262c7bd1170db69fafa5ef327 Author: James Cherry Date: Tue Feb 6 17:05:24 2024 -0700 arnoldi parasitic leak Signed-off-by: James Cherry commit eca0e8b5ea3b4dbb22a1a2ed11018e6e40229b3f Author: James Cherry Date: Tue Feb 6 14:40:38 2024 -0700 comment Signed-Off-by: James Cherry commit 0263245b5e2412ebefbedc67babf23e1ac047c7b Author: James Cherry Date: Tue Feb 6 14:24:51 2024 -0700 CouplingCap -> Capacitor Signed-off-by: James Cherry commit f9da059814fb09c44cc3529a9a787c3c2192a4e9 Author: James Cherry Date: Tue Feb 6 09:31:00 2024 -0700 rm parasitic network array if empty Signed-off-by: James Cherry commit 28c2728e5f2859839818ef228aac51fd0100ae65 Author: James Cherry Date: Tue Feb 6 08:13:03 2024 -0700 parasitic resistor name -> id Signed-off-by: James Cherry commit 045fd7efa3ae8b1cf07c5aa421f3119022e3895a Author: James Cherry Date: Mon Feb 5 21:09:39 2024 -0700 Map -> map Signed-off-by: James Cherry commit 8f7d18eed14a8173d91fd98a4e345a16d168b0ee Author: James Cherry Date: Mon Feb 5 21:04:35 2024 -0700 ParasiticResistor, ParasiticCapacitor Signed-off-by: James Cherry commit e2df87a10febc573c77b51a22e82d2d1f6f52af9 Author: James Cherry Date: Mon Feb 5 17:06:34 2024 -0700 rm ParasticNode::devices Signed-off-by: James Cherry commit 07133b72b73d204d16f964472c38907c18f9758d Author: James Cherry Date: Mon Feb 5 16:52:43 2024 -0700 Parsitic network nodes instead of nodeIterator Signed-off-by: James Cherry commit 48c08673b11d0c328ed7d70606b6c7a979d9d0b8 Author: James Cherry Date: Mon Feb 5 16:34:31 2024 -0700 mv otherNode to Parasitics Signed-off-by: James Cherry commit 99fccc76937c25c68454d8db667306bff2a142ae Author: James Cherry Date: Mon Feb 5 16:29:23 2024 -0700 ParasiticNetwork resistor/capacitor array Signed-off-by: James Cherry commit 9de49992ad403d7bc3468c53201d50825d7b961c Author: James Cherry Date: Mon Feb 5 09:42:01 2024 -0700 SpefNameMap Signed-off-by: James Cherry commit f296850201debeb2cfe1fd0b9c61c3c196f00d65 Author: James Cherry Date: Mon Feb 5 09:11:17 2024 -0700 comments Signed-off-by: James Cherry commit 86ca29b9bdeb732c1a596c196e0c4bf91de3ee37 Author: James Cherry Date: Mon Feb 5 08:29:53 2024 -0700 rm Parasitics::reduceTo Signed-off-by: James Cherry commit 880bf458d473004ee5d3dc33baa62c9e643ddaec Author: James Cherry Date: Sun Feb 4 20:15:05 2024 -0700 loadCap Signed-off-by: James Cherry commit 67322e686f4703a2a5d9cdd1dd66534814662fe4 Author: James Cherry Date: Sun Feb 4 09:39:21 2024 -0700 report_parasitic_annotation Signed-off-by: James Cherry commit 8ef4e9841bca62a5879e74da83cacee70fa50b2f Author: James Cherry Date: Sat Feb 3 19:13:27 2024 -0700 ParasiticAnalysisPt use string Signed-off-by: James Cherry commit 109a85ab37b5a869a72738ac6a6cd84e4a1d1ac4 Author: James Cherry Date: Sat Feb 3 18:59:02 2024 -0700 rm ParasiticAnalysisPt::min_max_ Signed-off-by: James Cherry commit bb7874537d20a1fe905779fe46d783dba14e2db6 Author: James Cherry Date: Sat Feb 3 12:21:28 2024 -0700 parasitics rm pole_residue pointer Signed-off-by: James Cherry commit 9e1e2c484e5cd088a08afc278f25b9fcf2cc5dd9 Author: James Cherry Date: Sat Feb 3 11:54:22 2024 -0700 parasitics rm loads pointer Signed-off-by: James Cherry commit cb4a7f870b2371a2ac6b3ce1d340bb5d3c24791a Author: James Cherry Date: Sat Feb 3 08:05:55 2024 -0700 parasitics use override Signed-off-by: James Cherry commit 8e0f84c4fec0411ad3626c836710545531ef219d Author: James Cherry Date: Sat Feb 3 07:53:59 2024 -0700 Parasitics::unannotatedLoads Signed-off-by: James Cherry commit 6b45e369e7be158616219258e6e9a675e87fd8ca Author: James Cherry Date: Fri Feb 2 12:27:23 2024 -0700 format Signed-off-by: James Cherry commit 27e820b36caf7867d20307c7045e86486819db6b Author: James Cherry Date: Thu Feb 1 18:01:51 2024 -0700 rm op_cond args Signed-off-by: James Cherry commit 351ed53925c7cc9815f75c34a0320b0dc50445d4 Author: James Cherry Date: Wed Jan 31 17:35:15 2024 -0700 rm GraphDelayCalc::loadPins() Signed-off-by: James Cherry commit 3341c7caff595dab0b7519ab5103958aadfe1510 Author: James Cherry Date: Wed Jan 31 17:31:56 2024 -0700 read_spef arg check Signed-off-by: James Cherry commit 7d0c1e78b42e33d5298efefa87a982f28f51bc57 Author: James Cherry Date: Wed Jan 31 10:53:35 2024 -0700 arnoldi use parasitics api Signed-off-by: James Cherry commit 86b39ac10e5c6556a9b0b5b7bce016884cd935ee Author: James Cherry Date: Wed Jan 31 10:30:47 2024 -0700 range iter Signed-off-by: James Cherry commit 469fad36af69cc8b76e4dfc88a085962795d7c46 Author: James Cherry Date: Tue Jan 30 16:43:46 2024 -0700 read_spef -reduce Signed-off-by: James Cherry commit 2b88aa471f083ae895f6277c2c844e308451fff9 Author: James Cherry Date: Mon Jan 29 20:31:47 2024 -0700 Paraasitics::connectionPin() -> pin() Signed-off-by: James Cherry commit 7b9ff7e228b215b3121b7e7189d9c0c18ced3ef3 Author: James Cherry Date: Mon Jan 29 17:12:32 2024 -0700 ParasiticNode::isExternal() Signed-off-by: James Cherry commit 889c27af846ed1cdf76295da5262836378ab9162 Author: James Cherry Date: Mon Jan 29 11:17:59 2024 -0700 rm redundant op_cond arg Signed-off-by: James Cherry commit 7d7ce5e7809bc80f36dd81cb05615a87433ed315 Author: James Cherry Date: Mon Jan 29 11:03:42 2024 -0700 mv estimatePiElmore to Parasitics Signed-off-by: James Cherry commit 04e1757b3c8b4e9f5cffbe3b03214fc065fb1c2c Author: James Cherry Date: Mon Jan 29 09:09:28 2024 -0700 ParasiticNode un-virtual Signed-off-by: James Cherry commit 016ce50f82cbb68f9536d3ed5fd511b2f82f4439 Author: James Cherry Date: Sun Jan 28 17:26:04 2024 -0700 parasitics coupling cap api Signed-off-by: James Cherry commit 1748629fb462b24b43002ecd3fe1679d367752f4 Author: James Cherry Date: Sun Jan 28 11:12:46 2024 -0700 Parasitics::value rm ap arg Signed-off-by: James Cherry commit 1272cb86bcae5960c9af7d589f99f1488aa0b322 Author: James Cherry Date: Sun Jan 28 11:10:57 2024 -0700 read_spef rm -quiet arg Signed-off-by: James Cherry commit 3d86a9d86115dde5f20eb4bb8ca15f0c85de5810 Author: James Cherry Date: Sun Jan 28 11:01:24 2024 -0700 reduce min_max arg Signed-off-by: James Cherry commit f7abfd5e72e0f74b9ffabf6306bbf809b62d4e98 Author: James Cherry Date: Sun Jan 28 10:59:29 2024 -0700 rm spef_reader Signed-off-by: James Cherry commit e3550523b1964b2137419240f748a0b44c3322b6 Author: James Cherry Date: Sun Jan 28 10:58:24 2024 -0700 reducers rm op_cond arg Signed-off-by: James Cherry commit cec793accb3db5c41cdb51f85c8530ffc1e085db Author: James Cherry Date: Sun Jan 28 10:08:45 2024 -0700 rm NullParastics Signed-off-by: James Cherry commit 6596d35f6da51cbacb2c21588715773d3b5edb64 Author: James Cherry Date: Sun Jan 28 10:03:29 2024 -0700 ArcDelayCalc::reduceParasitic Signed-off-by: James Cherry Signed-off-by: James Cherry --- CMakeLists.txt | 1 - dcalc/Arnoldi.hh | 2 + dcalc/ArnoldiDelayCalc.cc | 85 +- dcalc/ArnoldiReduce.cc | 146 +-- dcalc/ArnoldiReduce.hh | 20 +- dcalc/DelayCalcBase.cc | 36 +- dcalc/DelayCalcBase.hh | 13 +- dcalc/DmpDelayCalc.cc | 83 +- dcalc/GraphDelayCalc.cc | 210 ++-- dcalc/LumpedCapDelayCalc.cc | 78 +- dcalc/LumpedCapDelayCalc.hh | 7 +- dcalc/UnitDelayCalc.cc | 17 +- dcalc/UnitDelayCalc.hh | 9 +- doc/ChangeLog.txt | 6 + doc/OpenSTA.odt | Bin 105077 -> 105281 bytes doc/OpenSTA.pdf | Bin 250794 -> 251507 bytes include/sta/ArcDelayCalc.hh | 13 +- include/sta/Corner.hh | 5 +- include/sta/GraphDelayCalc.hh | 88 +- include/sta/NullParasitics.hh | 198 ---- include/sta/Parasitics.hh | 213 ++-- include/sta/ParasiticsClass.hh | 5 +- include/sta/Sdc.hh | 8 +- include/sta/Sta.hh | 15 +- parasitics/ConcreteParasitics.cc | 1237 +++++++++-------------- parasitics/ConcreteParasitics.hh | 293 +++--- parasitics/ConcreteParasiticsPvt.hh | 431 +++----- parasitics/EstimateParasitics.cc | 48 +- parasitics/EstimateParasitics.hh | 15 +- parasitics/NullParasitics.cc | 467 --------- parasitics/Parasitics.cc | 212 +++- parasitics/Parasitics.i | 8 +- parasitics/Parasitics.tcl | 35 +- parasitics/ReduceParasitics.cc | 428 ++++---- parasitics/ReduceParasitics.hh | 12 +- parasitics/ReportParasiticAnnotation.cc | 14 +- parasitics/SpefReader.cc | 234 ++--- parasitics/SpefReader.hh | 15 +- parasitics/SpefReaderPvt.hh | 45 +- sdc/Sdc.cc | 36 +- search/Corner.cc | 57 +- search/ReportPath.cc | 16 +- search/ReportPath.hh | 3 - search/Sta.cc | 44 +- search/WritePathSpice.cc | 150 +-- tcl/CmdArgs.tcl | 2 +- tcl/StaTcl.i | 6 + tcl/StaTclTypes.i | 15 - util/RiseFallMinMax.cc | 4 +- 49 files changed, 1886 insertions(+), 3199 deletions(-) delete mode 100644 include/sta/NullParasitics.hh delete mode 100644 parasitics/NullParasitics.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index bb1d30b0..2436b263 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,7 +115,6 @@ set(STA_SOURCE parasitics/ConcreteParasitics.cc parasitics/EstimateParasitics.cc - parasitics/NullParasitics.cc parasitics/Parasitics.cc parasitics/ReduceParasitics.cc parasitics/ReportParasiticAnnotation.cc diff --git a/dcalc/Arnoldi.hh b/dcalc/Arnoldi.hh index 545bec72..3dc238b1 100644 --- a/dcalc/Arnoldi.hh +++ b/dcalc/Arnoldi.hh @@ -64,6 +64,8 @@ public: rcmodel(); virtual ~rcmodel(); virtual float capacitance() const; + virtual PinSet unannotatedLoads(const Pin *drvr_pin, + const Parasitics *parasitics) const; const Pin **pinV; // [n] }; diff --git a/dcalc/ArnoldiDelayCalc.cc b/dcalc/ArnoldiDelayCalc.cc index 88d0c880..5cb2cf86 100644 --- a/dcalc/ArnoldiDelayCalc.cc +++ b/dcalc/ArnoldiDelayCalc.cc @@ -30,6 +30,7 @@ #include "TimingModel.hh" #include "TimingArc.hh" #include "TableModel.hh" +#include "PortDirection.hh" #include "Network.hh" #include "Graph.hh" #include "Parasitics.hh" @@ -117,7 +118,10 @@ public: Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; - ReducedParasiticType reducedParasiticType() const override; + Parasitic *reduceParasitic(const Parasitic *parasitic_network, + const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, @@ -140,6 +144,7 @@ public: const LoadPinIndexMap &load_pin_index_map, const DcalcAnalysisPt *dcalc_ap, int digits) override; + void finishDrvrPin() override; void delay_work_set_thresholds(delay_work *D, double lo, double hi, @@ -219,6 +224,7 @@ private: int pin_n_; ArnoldiReduce *reduce_; delay_work *delay_work_; + vector unsaved_parasitics_; }; ArcDelayCalc * @@ -259,49 +265,53 @@ ArnoldiDelayCalc::findParasitic(const Pin *drvr_pin, Parasitic *parasitic = nullptr; const Corner *corner = dcalc_ap->corner(); // set_load net has precedence over parasitics. - if (!sdc_->drvrPinHasWireCap(drvr_pin, corner)) { - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - Parasitic *parasitic_network = - parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - bool delete_parasitic_network = false; - - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); - if (parasitic_network == nullptr) { - Wireload *wireload = sdc_->wireload(cnst_min_max); - if (wireload) { - float pin_cap, wire_cap, fanout; - bool has_wire_cap; - graph_delay_calc_->netCaps(drvr_pin, drvr_rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_wire_cap); - parasitic_network = parasitics_->makeWireloadNetwork(drvr_pin, wireload, - fanout, op_cond, - parasitic_ap); - delete_parasitic_network = true; - } + if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + || network_->direction(drvr_pin)->isInternal()) + return nullptr; + const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + Parasitic *parasitic_network = + parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + const MinMax *min_max = dcalc_ap->constraintMinMax(); + if (parasitic_network == nullptr) { + Wireload *wireload = sdc_->wireload(min_max); + if (wireload) { + float pin_cap, wire_cap, fanout; + bool has_wire_cap; + graph_delay_calc_->netCaps(drvr_pin, drvr_rf, dcalc_ap, + pin_cap, wire_cap, fanout, has_wire_cap); + parasitic_network = parasitics_->makeWireloadNetwork(drvr_pin, wireload, + fanout, min_max, + parasitic_ap); } + } - if (parasitic_network) { - parasitic = reduce_->reduceToArnoldi(parasitic_network, - drvr_pin, - parasitic_ap->couplingCapFactor(), - drvr_rf, op_cond, corner, - cnst_min_max, parasitic_ap); - if (delete_parasitic_network) { - Net *net = network_->net(drvr_pin); - parasitics_->deleteParasiticNetwork(net, parasitic_ap); - } - // Arnoldi parasitics are their own class that are not saved in the parasitic db. - unsaved_parasitics_.push_back(parasitic); - } + if (parasitic_network) { + rcmodel *rcmodel = reduce_->reduceToArnoldi(parasitic_network, drvr_pin, + parasitic_ap->couplingCapFactor(), + drvr_rf, corner, min_max, parasitic_ap); + // Arnoldi parasitics are their own class that are not saved in the parasitic db. + unsaved_parasitics_.push_back(rcmodel); + parasitic = rcmodel; } return parasitic; } -ReducedParasiticType -ArnoldiDelayCalc::reducedParasiticType() const +Parasitic * +ArnoldiDelayCalc::reduceParasitic(const Parasitic *, + const Pin *, + const RiseFall *, + const DcalcAnalysisPt *) { - return ReducedParasiticType::arnoldi; + // Decline because reduced arnoldi parasitics are not stored in the parasitics db. + return nullptr; +} + +void +ArnoldiDelayCalc::finishDrvrPin() +{ + for (auto parasitic : unsaved_parasitics_) + delete parasitic; + unsaved_parasitics_.clear(); } ArcDcalcResult @@ -1304,7 +1314,6 @@ ArnoldiDelayCalc::ra_get_r(delay_work *D, r = tlohi/(c_log*c1); if (rdelay>0.0 && r > rdelay) r = rdelay; - // else printf("from rdelay %g to r %g\n",rdelay,r); return r; } diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index 1a0779d7..5c80c382 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -46,6 +46,14 @@ rcmodel::capacitance() const return ctot; } +PinSet +rcmodel::unannotatedLoads(const Pin *, + const Parasitics *) const +{ + // This should never be called because the rcmodel is not saved in the Parasitics. + return PinSet(); +} + struct ts_point { ParasiticNode *node_; @@ -62,7 +70,7 @@ struct ts_point struct ts_edge { - ConcreteParasiticResistor *resistor_; + ParasiticResistor *resistor_; ts_point *from; ts_point *to; }; @@ -131,23 +139,21 @@ ArnoldiReduce::~ArnoldiReduce() free(ts_pointV); } -Parasitic * +rcmodel * ArnoldiReduce::reduceToArnoldi(Parasitic *parasitic, const Pin *drvr_pin, float coupling_cap_factor, const RiseFall *rf, - const OperatingConditions *op_cond, const Corner *corner, - const MinMax *cnst_min_max, + const MinMax *min_max, const ParasiticAnalysisPt *ap) { parasitic_network_ = reinterpret_cast(parasitic); drvr_pin_ = drvr_pin; coupling_cap_factor_ = coupling_cap_factor; rf_ = rf; - op_cond_ = op_cond; corner_ = corner; - cnst_min_max_ = cnst_min_max; + min_max_ = min_max; ap_ = ap; loadWork(); return makeRcmodelDrv(); @@ -158,18 +164,21 @@ ArnoldiReduce::loadWork() { pt_map_.clear(); - int resistor_count = 0; - ConcreteParasiticDeviceSet devices; - parasitic_network_->devices(&devices); - ConcreteParasiticDeviceSet::Iterator device_iter(devices); - while (device_iter.hasNext()) { - ParasiticDevice *device = device_iter.next(); - if (parasitics_->isResistor(device)) - resistor_count++; - } + const ParasiticResistorSeq &resistors = parasitics_->resistors(parasitic_network_); + int resistor_count = resistors.size(); - termN = parasitic_network_->pinNodes()->size(); - int subnode_count = parasitic_network_->subNodes()->size(); + termN = 0; + int subnode_count = 0; + ParasiticNodeSeq nodes = parasitics_->nodes(parasitic_network_); + for (ParasiticNode *node : nodes) { + if (!parasitics_->isExternal(node)) { + const Pin *pin = parasitics_->pin(node); + if (pin) + termN++; + else + subnode_count++; + } + } ts_pointN = subnode_count + 1 + termN; ts_edgeN = resistor_count; allocPoints(); @@ -191,50 +200,42 @@ ArnoldiReduce::loadWork() pend = pterm0; e = e0; int index = 0; - ConcreteParasiticSubNodeMap::Iterator - sub_node_iter(parasitic_network_->subNodes()); - while (sub_node_iter.hasNext()) { - ConcreteParasiticSubNode *node = sub_node_iter.next(); - pt_map_[node] = index; - p = p0 + index; - p->node_ = node; - p->eN = 0; - p->is_term = false; - index++; + + for (ParasiticNode *node : nodes) { + if (!parasitics_->isExternal(node)) { + const Pin *pin = parasitics_->pin(node); + if (pin) { + p = pend++; + pt_map_[node] = p - p0; + p->node_ = node; + p->eN = 0; + p->is_term = true; + tindex = p - pterm0; + p->tindex = tindex; + pinV[tindex] = pin; + } + else { + pt_map_[node] = index; + p = p0 + index; + p->node_ = node; + p->eN = 0; + p->is_term = false; + index++; + } + } } - ConcreteParasiticPinNodeMap::Iterator - pin_node_iter(parasitic_network_->pinNodes()); - while (pin_node_iter.hasNext()) { - ConcreteParasiticPinNode *node = pin_node_iter.next(); - p = pend++; - pt_map_[node] = p - p0; - p->node_ = node; - p->eN = 0; - p->is_term = true; - tindex = p - pterm0; - p->tindex = tindex; - const Pin *pin = parasitics_->connectionPin(node); - pinV[tindex] = pin; - } - ts_edge **eV = ts_eV; - ConcreteParasiticDeviceSet::Iterator device_iter2(devices); - while (device_iter2.hasNext()) { - ParasiticDevice *device = device_iter2.next(); - if (parasitics_->isResistor(device)) { - ConcreteParasiticResistor *resistor = - reinterpret_cast(device); - ts_point *pt1 = findPt(resistor->node1()); - ts_point *pt2 = findPt(resistor->node2()); - e->from = pt1; - e->to = pt2; - e->resistor_ = resistor; - pt1->eN++; - if (e->from != e->to) - pt2->eN++; - e++; - } + for (ParasiticResistor *resistor : resistors) { + ts_point *pt1 = findPt(parasitics_->node1(resistor)); + ts_point *pt2 = findPt(parasitics_->node2(resistor)); + e->from = pt1; + e->to = pt2; + e->resistor_ = resistor; + pt1->eN++; + if (e->from != e->to) + pt2->eN++; + e++; } for (p=p0;p!=pend;p++) { @@ -313,8 +314,7 @@ ArnoldiReduce::findPt(ParasiticNode *node) rcmodel * ArnoldiReduce::makeRcmodelDrv() { - ParasiticNode *drv_node = parasitics_->findNode(parasitic_network_, - drvr_pin_); + ParasiticNode *drv_node = parasitics_->findNode(parasitic_network_, drvr_pin_); ts_point *pdrv = findPt(drv_node); makeRcmodelDfs(pdrv); getRC(); @@ -322,8 +322,7 @@ ArnoldiReduce::makeRcmodelDrv() return nullptr; setTerms(pdrv); makeRcmodelFromTs(); - rcmodel *mod = makeRcmodelFromW(); - return mod; + return makeRcmodelFromW(); } #define ts_orient( pp, ee) \ @@ -415,7 +414,7 @@ ArnoldiReduce::getRC() p->r = 0.0; if (p->node_) { ParasiticNode *node = p->node_; - double cap = parasitics_->nodeGndCap(node, ap_) + double cap = parasitics_->nodeGndCap(node) + pinCapacitance(node); if (cap > 0.0) { p->c = cap; @@ -424,7 +423,7 @@ ArnoldiReduce::getRC() else p->c = 0.0; if (p->in_edge && p->in_edge->resistor_) - p->r = parasitics_->value(p->in_edge->resistor_, ap_); + 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", @@ -433,20 +432,33 @@ ArnoldiReduce::getRC() } } } + for (ParasiticCapacitor *capacitor : parasitics_->capacitors(parasitic_network_)) { + float cap = parasitics_->value(capacitor) * ap_->couplingCapFactor(); + ParasiticNode *node1 = parasitics_->node1(capacitor); + if (!parasitics_->isExternal(node1)) { + ts_point *pt = findPt(node1); + pt->c += cap; + } + ParasiticNode *node2 = parasitics_->node2(capacitor); + if (!parasitics_->isExternal(node2)) { + ts_point *pt = findPt(node2); + pt->c += cap; + } + } } float ArnoldiReduce::pinCapacitance(ParasiticNode *node) { - const Pin *pin = parasitics_->connectionPin(node); + const Pin *pin = parasitics_->pin(node); float pin_cap = 0.0; if (pin) { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(port); if (lib_port) - pin_cap = sdc_->pinCapacitance(pin,rf_, op_cond_, corner_, cnst_min_max_); + pin_cap = sdc_->pinCapacitance(pin,rf_, corner_, min_max_); else if (network_->isTopLevelPort(pin)) - pin_cap = sdc_->portExtCap(port, rf_, corner_, cnst_min_max_); + pin_cap = sdc_->portExtCap(port, rf_, corner_, min_max_); } return pin_cap; } diff --git a/dcalc/ArnoldiReduce.hh b/dcalc/ArnoldiReduce.hh index c5e1a004..ad49da34 100644 --- a/dcalc/ArnoldiReduce.hh +++ b/dcalc/ArnoldiReduce.hh @@ -37,21 +37,20 @@ class rcmodel; struct ts_edge; struct ts_point; -typedef Map ArnolidPtMap; +typedef Map ArnolidPtMap; class ArnoldiReduce : public StaState { public: ArnoldiReduce(StaState *sta); ~ArnoldiReduce(); - Parasitic *reduceToArnoldi(Parasitic *parasitic, - const Pin *drvr_pin, - float coupling_cap_factor, - const RiseFall *rf, - const OperatingConditions *op_cond, - const Corner *corner, - const MinMax *cnst_min_max, - const ParasiticAnalysisPt *ap); + rcmodel *reduceToArnoldi(Parasitic *parasitic, + const Pin *drvr_pin, + float coupling_cap_factor, + const RiseFall *rf, + const Corner *corner, + const MinMax *cnst_min_max, + const ParasiticAnalysisPt *ap); protected: void loadWork(); @@ -70,9 +69,8 @@ protected: const Pin *drvr_pin_; float coupling_cap_factor_; const RiseFall *rf_; - const OperatingConditions *op_cond_; const Corner *corner_; - const MinMax *cnst_min_max_; + const MinMax *min_max_; const ParasiticAnalysisPt *ap_; // ParasiticNode -> ts_point index. ArnolidPtMap pt_map_; diff --git a/dcalc/DelayCalcBase.cc b/dcalc/DelayCalcBase.cc index ea748e8d..7db5663f 100644 --- a/dcalc/DelayCalcBase.cc +++ b/dcalc/DelayCalcBase.cc @@ -23,6 +23,7 @@ #include "Network.hh" #include "Parasitics.hh" #include "Sdc.hh" +#include "Corner.hh" #include "DcalcAnalysisPt.hh" namespace sta { @@ -34,6 +35,35 @@ DelayCalcBase::DelayCalcBase(StaState *sta) : { } +void +DelayCalcBase::reduceParasitic(const Parasitic *parasitic_network, + const Net *net, + const Corner *corner, + const MinMaxAll *min_max) +{ + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + if (network_->isDriver(pin)) { + for (RiseFall *rf : RiseFall::range()) { + for (const MinMax *min_max : min_max->range()) { + if (corner == nullptr) { + for (const Corner *corner1 : *corners_) { + DcalcAnalysisPt *dcalc_ap = corner1->findDcalcAnalysisPt(min_max); + reduceParasitic(parasitic_network, pin, rf, dcalc_ap); + } + } + else { + DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max); + reduceParasitic(parasitic_network, pin, rf, dcalc_ap); + } + } + } + } + } + delete pin_iter; +} + TimingModel * DelayCalcBase::model(const TimingArc *arc, const DcalcAnalysisPt *dcalc_ap) const @@ -67,12 +97,6 @@ DelayCalcBase::checkModel(const TimingArc *arc, void DelayCalcBase::finishDrvrPin() { - for (auto parasitic : unsaved_parasitics_) - parasitics_->deleteUnsavedParasitic(parasitic); - unsaved_parasitics_.clear(); - for (auto drvr_pin : reduced_parasitic_drvrs_) - parasitics_->deleteDrvrReducedParasitics(drvr_pin); - reduced_parasitic_drvrs_.clear(); } // For DSPF on an input port the elmore delay is used as the time diff --git a/dcalc/DelayCalcBase.hh b/dcalc/DelayCalcBase.hh index 1c7e8173..76137d0b 100644 --- a/dcalc/DelayCalcBase.hh +++ b/dcalc/DelayCalcBase.hh @@ -28,13 +28,18 @@ public: explicit DelayCalcBase(StaState *sta); void finishDrvrPin() override; + void reduceParasitic(const Parasitic *parasitic_network, + const Net *net, + const Corner *corner, + const MinMaxAll *min_max) override; + ArcDelay checkDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, const Slew &to_slew, float related_out_cap, const DcalcAnalysisPt *dcalc_ap) override; - + string reportCheckDelay(const Pin *check_pin, const TimingArc *arc, const Slew &from_slew, @@ -72,11 +77,7 @@ protected: const Pvt *pinPvt(const Pin *pin, const DcalcAnalysisPt *dcalc_ap); - // Parasitics returned by findParasitic that are reduced or estimated - // that can be deleted after delay calculation for the driver pin - // is finished. - Vector unsaved_parasitics_; - Vector reduced_parasitic_drvrs_; + using ArcDelayCalc::reduceParasitic; }; } // namespace diff --git a/dcalc/DmpDelayCalc.cc b/dcalc/DmpDelayCalc.cc index 15145abd..8006a0f8 100644 --- a/dcalc/DmpDelayCalc.cc +++ b/dcalc/DmpDelayCalc.cc @@ -19,12 +19,13 @@ #include "TableModel.hh" #include "TimingArc.hh" #include "Liberty.hh" +#include "PortDirection.hh" +#include "Network.hh" #include "Sdc.hh" #include "Parasitics.hh" #include "DcalcAnalysisPt.hh" #include "GraphDelayCalc.hh" #include "DmpCeff.hh" -#include "Network.hh" namespace sta { @@ -132,7 +133,6 @@ public: Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; - ReducedParasiticType reducedParasiticType() const override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, @@ -210,58 +210,41 @@ DmpCeffTwoPoleDelayCalc::findParasitic(const Pin *drvr_pin, Parasitic *parasitic = nullptr; const Corner *corner = dcalc_ap->corner(); // set_load net has precedence over parasitics. - if (!sdc_->drvrPinHasWireCap(drvr_pin, corner)) { - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - if (parasitics_->haveParasitics()) { - // Prefer PiPoleResidue. - parasitic = parasitics_->findPiPoleResidue(drvr_pin, rf, parasitic_ap); - if (parasitic == nullptr) { - parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); - if (parasitic == nullptr) { - Parasitic *parasitic_network = - parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - if (parasitic_network) { - parasitics_->reduceToPiPoleResidue2(parasitic_network, drvr_pin, - dcalc_ap->operatingConditions(), - corner, - dcalc_ap->constraintMinMax(), - parasitic_ap); - parasitic = parasitics_->findPiPoleResidue(drvr_pin, rf, parasitic_ap); - reduced_parasitic_drvrs_.push_back(drvr_pin); - } - } - } - } - else { - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - Wireload *wireload = sdc_->wireload(cnst_min_max); - if (wireload) { - float pin_cap, wire_cap, fanout; - bool has_wire_cap; - graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_wire_cap); - parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, - fanout, pin_cap, - dcalc_ap->operatingConditions(), - corner, - cnst_min_max, - parasitic_ap); - // Estimated parasitics are not recorded in the "database", so - // save it for deletion after the drvr pin delay calc is finished. - if (parasitic) - unsaved_parasitics_.push_back(parasitic); - } - } + if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + || network_->direction(drvr_pin)->isInternal()) + return nullptr; + const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + // Prefer PiPoleResidue. + parasitic = parasitics_->findPiPoleResidue(drvr_pin, rf, parasitic_ap); + if (parasitic) + return parasitic; + parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); + if (parasitic) + return parasitic; + Parasitic *parasitic_network = + parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); + if (parasitic_network) { + parasitic = parasitics_->reduceToPiPoleResidue2(parasitic_network, drvr_pin, rf, + corner, + dcalc_ap->constraintMinMax(), + parasitic_ap); + if (parasitic) + return parasitic; + } + const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); + Wireload *wireload = sdc_->wireload(cnst_min_max); + if (wireload) { + float pin_cap, wire_cap, fanout; + bool has_wire_cap; + graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap, + fanout, has_wire_cap); + parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, + fanout, pin_cap, corner, + cnst_min_max); } return parasitic; } -ReducedParasiticType -DmpCeffTwoPoleDelayCalc::reducedParasiticType() const -{ - return ReducedParasiticType::pi_pole_residue2; -} - ArcDcalcResult DmpCeffTwoPoleDelayCalc::inputPortDelay(const Pin *, float in_slew, diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index ba9f1463..da3b40d0 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -365,9 +365,11 @@ GraphDelayCalc::seedNoDrvrCellSlew(Vertex *drvr_vertex, Delay drive_delay = delay_zero; float drive_res; drive->driveResistance(rf, cnst_min_max, drive_res, exists); - Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); + const Parasitic *parasitic; + float cap; + parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc, + cap, parasitic); if (exists) { - float cap = loadCap(drvr_pin, parasitic, rf, dcalc_ap); drive_delay = cap * drive_res; slew = cap * drive_res; } @@ -408,7 +410,7 @@ GraphDelayCalc::seedNoDrvrSlew(Vertex *drvr_vertex, load_pin_index_map, dcalc_ap); annotateLoadDelays(drvr_vertex, rf, dcalc_result, load_pin_index_map, delay_zero, false, dcalc_ap); - arc_delay_calc->finishDrvrPin(); + arc_delay_calc_->finishDrvrPin(); } void @@ -500,6 +502,7 @@ GraphDelayCalc::findInputDriverDelay(const LibertyCell *drvr_cell, } } } + arc_delay_calc_->finishDrvrPin(); } // Driving cell delay is the load dependent delay, which is the gate @@ -521,8 +524,10 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); if (drvr_rf) { DcalcAPIndex ap_index = dcalc_ap->index(); - Parasitic *parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, dcalc_ap); - float load_cap = loadCap(drvr_pin, parasitic, drvr_rf, dcalc_ap); + const Parasitic *parasitic; + float load_cap; + parasiticLoad(drvr_pin, drvr_rf, dcalc_ap, nullptr, arc_delay_calc_, + load_cap, parasitic); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); ArcDcalcResult intrinsic_result = @@ -547,6 +552,7 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, graph_->setSlew(drvr_vertex, drvr_rf, ap_index, gate_slew); annotateLoadDelays(drvr_vertex, drvr_rf, gate_result, load_pin_index_map, load_delay, false, dcalc_ap); + arc_delay_calc_->finishDrvrPin(); } } @@ -646,7 +652,7 @@ GraphDelayCalc::findDriverDelays(Vertex *drvr_vertex, initLoadSlews(drvr_vertex); delay_changed |= findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc); } - arc_delay_calc->finishDrvrPin(); + arc_delay_calc_->finishDrvrPin(); return delay_changed; } @@ -849,7 +855,6 @@ GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex, Vertex *from_vertex = edge->from(graph_); const TimingArcSet *arc_set = edge->timingArcSet(); bool delay_changed = false; - PinSeq load_pins = loadPins(drvr_vertex); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); for (auto dcalc_ap : corners_->dcalcAnalysisPts()) { for (const TimingArc *arc : arc_set->arcs()) @@ -892,9 +897,11 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, const RiseFall *drvr_rf = arc->toEdge()->asRiseFall(); if (from_rf && drvr_rf) { const Pin *drvr_pin = drvr_vertex->pin(); - Parasitic *parasitic = arc_delay_calc->findParasitic(drvr_pin, drvr_rf, - dcalc_ap); - float load_cap = loadCap(drvr_pin, parasitic, drvr_rf, dcalc_ap, multi_drvr); + const Parasitic *parasitic; + float load_cap; + parasiticLoad(drvr_pin, drvr_rf, dcalc_ap, multi_drvr, arc_delay_calc, + load_cap, parasitic); + if (multi_drvr && multi_drvr->parallelGates(network_)) { ArcDcalcArgSeq dcalc_args = makeArcDcalcArgs(drvr_vertex, multi_drvr, @@ -921,6 +928,7 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, load_pin_index_map, dcalc_ap); } + arc_delay_calc->finishDrvrPin(); } return delay_changed; } @@ -1126,21 +1134,6 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, } } -PinSeq -GraphDelayCalc::loadPins(Vertex *drvr_vertex) -{ - PinSeq load_pins; - VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); - while (edge_iter.hasNext()) { - Edge *wire_edge = edge_iter.next(); - if (wire_edge->isWire()) { - Vertex *load_vertex = wire_edge->to(graph_); - load_pins.push_back(load_vertex->pin()); - } - } - return load_pins; -} - LoadPinIndexMap GraphDelayCalc::makeLoadPinIndexMap(Vertex *drvr_vertex) { @@ -1159,6 +1152,7 @@ GraphDelayCalc::makeLoadPinIndexMap(Vertex *drvr_vertex) return load_pin_index_map; } +// External float GraphDelayCalc::loadCap(const Pin *drvr_pin, const DcalcAnalysisPt *dcalc_ap) const @@ -1166,92 +1160,104 @@ GraphDelayCalc::loadCap(const Pin *drvr_pin, const MinMax *min_max = dcalc_ap->constraintMinMax(); float load_cap = 0.0; for (auto drvr_rf : RiseFall::range()) { - Parasitic *parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, dcalc_ap); - float cap = loadCap(drvr_pin, parasitic, drvr_rf, dcalc_ap, nullptr); - arc_delay_calc_->finishDrvrPin(); + float cap = loadCap(drvr_pin, drvr_rf, dcalc_ap); if (min_max->compare(cap, load_cap)) load_cap = cap; } + arc_delay_calc_->finishDrvrPin(); return load_cap; } +// External float GraphDelayCalc::loadCap(const Pin *drvr_pin, - const RiseFall *drvr_rf, - const DcalcAnalysisPt *dcalc_ap) const -{ - Parasitic *parasitic = arc_delay_calc_->findParasitic(drvr_pin, drvr_rf, - dcalc_ap); - float cap = loadCap(drvr_pin, parasitic, drvr_rf, dcalc_ap, nullptr); - return cap; -} - -float -GraphDelayCalc::loadCap(const Pin *drvr_pin, - const Parasitic *parasitic, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) const -{ - return loadCap(drvr_pin, parasitic, rf, dcalc_ap, nullptr); -} - -float -GraphDelayCalc::loadCap(const Pin *drvr_pin, - const Parasitic *parasitic, - const RiseFall *rf, - const DcalcAnalysisPt *dcalc_ap, - const MultiDrvrNet *multi_drvr) const { float pin_cap, wire_cap; - bool has_net_load; - float fanout; - if (multi_drvr) - multi_drvr->netCaps(rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_net_load); - else - netCaps(drvr_pin, rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_net_load); - loadCap(parasitic, has_net_load, pin_cap, wire_cap); - return wire_cap + pin_cap; + loadCap(drvr_pin, rf, dcalc_ap, pin_cap, wire_cap); + return pin_cap + wire_cap; } +// External void GraphDelayCalc::loadCap(const Pin *drvr_pin, - const Parasitic *parasitic, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap, - // Return values. float &pin_cap, float &wire_cap) const { - bool has_net_load; - float fanout; - // Find pin and external pin/wire capacitance. - netCaps(drvr_pin, rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_net_load); - loadCap(parasitic, has_net_load, pin_cap, wire_cap); + MultiDrvrNet *multi_drvr = nullptr; + if (graph_) { + Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); + multi_drvr = multiDrvrNet(drvr_vertex); + } + const Parasitic *parasitic; + parasiticLoad(drvr_pin, rf, dcalc_ap, multi_drvr, arc_delay_calc_, + pin_cap, wire_cap, parasitic); + arc_delay_calc_->finishDrvrPin(); +} + +float +GraphDelayCalc::loadCap(const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + ArcDelayCalc *arc_delay_calc) const +{ + const Parasitic *parasitic; + float pin_cap, wire_cap; + parasiticLoad(drvr_pin, rf, dcalc_ap, nullptr, arc_delay_calc, + pin_cap, wire_cap, parasitic); + return pin_cap + wire_cap; } void -GraphDelayCalc::loadCap(const Parasitic *parasitic, - bool has_net_load, - // Return values. - float &pin_cap, - float &wire_cap) const +GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + const MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc, + // Return values. + float &load_cap, + const Parasitic *¶sitic) const { + float pin_cap, wire_cap; + parasiticLoad(drvr_pin, rf, dcalc_ap, multi_drvr, arc_delay_calc, + pin_cap, wire_cap, parasitic); + load_cap = pin_cap + wire_cap; +} + +void +GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + const MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc, + // Return values. + float &pin_cap, + float &wire_cap, + const Parasitic *¶sitic) const +{ + bool has_net_load; + float fanout; + netCaps(drvr_pin, rf, dcalc_ap, multi_drvr, + pin_cap, wire_cap, fanout, has_net_load); + + parasitic = arc_delay_calc->findParasitic(drvr_pin, rf, dcalc_ap); // set_load net has precedence over parasitics. if (!has_net_load && parasitic) { if (parasitics_->isParasiticNetwork(parasitic)) wire_cap += parasitics_->capacitance(parasitic); else { // PiModel includes both pin and external caps. - float cap = parasitics_->capacitance(parasitic); - if (pin_cap > cap) { - pin_cap = 0.0; - wire_cap = cap; + float parasitic_cap = parasitics_->capacitance(parasitic); + if (parasitic_cap >= pin_cap) + wire_cap = parasitic_cap - pin_cap; + else { + wire_cap = 0.0; + // Ignore parasitic if pin cap is greater. + parasitic = nullptr; } - else - wire_cap = cap - pin_cap; } } } @@ -1271,15 +1277,29 @@ GraphDelayCalc::netCaps(const Pin *drvr_pin, Vertex *drvr_vertex = graph_->pinDrvrVertex(drvr_pin); multi_drvr = multiDrvrNet(drvr_vertex); } + netCaps(drvr_pin, rf, dcalc_ap, multi_drvr, + pin_cap, wire_cap, fanout, has_net_load); +} + +void +GraphDelayCalc::netCaps(const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap, + const MultiDrvrNet *multi_drvr, + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load) const +{ if (multi_drvr) multi_drvr->netCaps(rf, dcalc_ap, pin_cap, wire_cap, fanout, has_net_load); else { - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); const Corner *corner = dcalc_ap->corner(); const MinMax *min_max = dcalc_ap->constraintMinMax(); // Find pin and external pin/wire capacitance. - sdc_->connectedCap(drvr_pin, rf, op_cond, corner, min_max, + sdc_->connectedCap(drvr_pin, rf, corner, min_max, pin_cap, wire_cap, fanout, has_net_load); } } @@ -1405,13 +1425,8 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, delayAsString(from_slew, this), delayAsString(to_slew, this)); float related_out_cap = 0.0; - if (related_out_pin) { - Parasitic *related_out_parasitic = - arc_delay_calc->findParasitic(related_out_pin, to_rf, dcalc_ap); - related_out_cap = loadCap(related_out_pin, - related_out_parasitic, - to_rf, dcalc_ap); - } + if (related_out_pin) + related_out_cap = loadCap(related_out_pin, to_rf,dcalc_ap,arc_delay_calc); ArcDelay check_delay = arc_delay_calc->checkDelay(to_pin, arc, from_slew, to_slew, related_out_cap, dcalc_ap); @@ -1420,6 +1435,7 @@ GraphDelayCalc::findCheckEdgeDelays(Edge *edge, delayAsString(check_delay, this)); graph_->setArcDelay(edge, arc, ap_index, check_delay); delay_changed = true; + arc_delay_calc_->finishDrvrPin(); } } } @@ -1467,12 +1483,8 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, if (related_out_port) related_out_pin = network_->findPin(inst, related_out_port); float related_out_cap = 0.0; - if (related_out_pin) { - Parasitic *related_out_parasitic = - arc_delay_calc_->findParasitic(related_out_pin, to_rf, dcalc_ap); - related_out_cap = loadCap(related_out_pin, related_out_parasitic, - to_rf, dcalc_ap); - } + if (related_out_pin) + related_out_cap = loadCap(related_out_pin, to_rf, dcalc_ap, arc_delay_calc_); if (role->isTimingCheck()) { const Slew &from_slew = checkEdgeClkSlew(from_vertex, from_rf, dcalc_ap); int slew_index = dcalc_ap->checkDataSlewIndex(); @@ -1484,10 +1496,11 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, related_out_cap, dcalc_ap, digits); } else { - Parasitic *to_parasitic = - arc_delay_calc_->findParasitic(to_pin, to_rf, dcalc_ap); const Slew &from_slew = edgeFromSlew(from_vertex, from_rf, edge, dcalc_ap); - float load_cap = loadCap(to_pin, to_parasitic, to_rf, dcalc_ap); + const Parasitic *to_parasitic; + float load_cap; + parasiticLoad(to_pin, to_rf, dcalc_ap, nullptr, arc_delay_calc_, + load_cap, to_parasitic); LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(to_vertex); result = arc_delay_calc_->reportGateDelay(to_pin, arc, from_slew, load_cap, to_parasitic, load_pin_index_map, @@ -1591,7 +1604,6 @@ MultiDrvrNet::findCaps(const Sdc *sdc) for (auto dcalc_ap : corners->dcalcAnalysisPts()) { DcalcAPIndex ap_index = dcalc_ap->index(); const Corner *corner = dcalc_ap->corner(); - const OperatingConditions *op_cond = dcalc_ap->operatingConditions(); const MinMax *min_max = dcalc_ap->constraintMinMax(); for (auto drvr_rf : RiseFall::range()) { int drvr_rf_index = drvr_rf->index(); @@ -1600,7 +1612,7 @@ MultiDrvrNet::findCaps(const Sdc *sdc) float pin_cap, wire_cap, fanout; bool has_net_load; // Find pin and external pin/wire capacitance. - sdc->connectedCap(drvr_pin, drvr_rf, op_cond, corner, min_max, + sdc->connectedCap(drvr_pin, drvr_rf, corner, min_max, pin_cap, wire_cap, fanout, has_net_load); net_caps.init(pin_cap, wire_cap, fanout, has_net_load); } diff --git a/dcalc/LumpedCapDelayCalc.cc b/dcalc/LumpedCapDelayCalc.cc index 975ca045..67cecec0 100644 --- a/dcalc/LumpedCapDelayCalc.cc +++ b/dcalc/LumpedCapDelayCalc.cc @@ -23,6 +23,7 @@ #include "TimingArc.hh" #include "TimingModel.hh" #include "Liberty.hh" +#include "PortDirection.hh" #include "Network.hh" #include "Sdc.hh" #include "Parasitics.hh" @@ -58,53 +59,46 @@ LumpedCapDelayCalc::findParasitic(const Pin *drvr_pin, Parasitic *parasitic = nullptr; const Corner *corner = dcalc_ap->corner(); // set_load net has precedence over parasitics. - if (!sdc_->drvrPinHasWireCap(drvr_pin, corner)) { - const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); - if (parasitics_->haveParasitics()) { - // Prefer PiElmore. - parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); - if (parasitic == nullptr) { - Parasitic *parasitic_network = - parasitics_->findParasiticNetwork(drvr_pin, parasitic_ap); - if (parasitic_network) { - parasitics_->reduceToPiElmore(parasitic_network, drvr_pin, - dcalc_ap->operatingConditions(), - corner, - dcalc_ap->constraintMinMax(), - parasitic_ap); - parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); - reduced_parasitic_drvrs_.push_back(drvr_pin); - } - } - } - else { - const MinMax *cnst_min_max = dcalc_ap->constraintMinMax(); - Wireload *wireload = sdc_->wireload(cnst_min_max); - if (wireload) { - float pin_cap, wire_cap, fanout; - bool has_net_load; - graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, - pin_cap, wire_cap, fanout, has_net_load); - parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, - fanout, pin_cap, - dcalc_ap->operatingConditions(), - corner, - cnst_min_max, - parasitic_ap); - // Estimated parasitics are not recorded in the "database", so save - // it for deletion after the drvr pin delay calc is finished. - if (parasitic) - unsaved_parasitics_.push_back(parasitic); - } - } + if (sdc_->drvrPinHasWireCap(drvr_pin, corner) + || network_->direction(drvr_pin)->isInternal()) + return nullptr; + const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + // Prefer PiElmore. + parasitic = parasitics_->findPiElmore(drvr_pin, rf, parasitic_ap); + if (parasitic) + return parasitic; + Parasitic *parasitic_network = parasitics_->findParasiticNetwork(drvr_pin, + parasitic_ap); + if (parasitic_network) { + parasitic = reduceParasitic(parasitic_network, drvr_pin, rf, dcalc_ap); + if (parasitic) + return parasitic; + } + const MinMax *min_max = dcalc_ap->constraintMinMax(); + Wireload *wireload = sdc_->wireload(min_max); + if (wireload) { + float pin_cap, wire_cap, fanout; + bool has_net_load; + graph_delay_calc_->netCaps(drvr_pin, rf, dcalc_ap, + pin_cap, wire_cap, fanout, has_net_load); + parasitic = parasitics_->estimatePiElmore(drvr_pin, rf, wireload, fanout, + pin_cap, corner, min_max); } return parasitic; } -ReducedParasiticType -LumpedCapDelayCalc::reducedParasiticType() const +Parasitic * +LumpedCapDelayCalc::reduceParasitic(const Parasitic *parasitic_network, + const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap) + { - return ReducedParasiticType::pi_elmore; + const Corner *corner = dcalc_ap->corner(); + const ParasiticAnalysisPt *parasitic_ap = dcalc_ap->parasiticAnalysisPt(); + return parasitics_->reduceToPiElmore(parasitic_network, drvr_pin, rf, + corner, dcalc_ap->constraintMinMax(), + parasitic_ap); } ArcDcalcResult diff --git a/dcalc/LumpedCapDelayCalc.hh b/dcalc/LumpedCapDelayCalc.hh index 0968c494..b62a2faf 100644 --- a/dcalc/LumpedCapDelayCalc.hh +++ b/dcalc/LumpedCapDelayCalc.hh @@ -30,7 +30,10 @@ public: Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; - ReducedParasiticType reducedParasiticType() const override; + Parasitic *reduceParasitic(const Parasitic *parasitic_network, + const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, @@ -59,6 +62,8 @@ protected: ArcDelay gate_delay, Slew drvr_slew, const LoadPinIndexMap &load_pin_index_map); + + using ArcDelayCalc::reduceParasitic; }; ArcDelayCalc * diff --git a/dcalc/UnitDelayCalc.cc b/dcalc/UnitDelayCalc.cc index d9ce6c7a..39c02fb5 100644 --- a/dcalc/UnitDelayCalc.cc +++ b/dcalc/UnitDelayCalc.cc @@ -45,10 +45,21 @@ UnitDelayCalc::findParasitic(const Pin *, return nullptr; } -ReducedParasiticType -UnitDelayCalc::reducedParasiticType() const +Parasitic * +UnitDelayCalc::reduceParasitic(const Parasitic *, + const Pin *, + const RiseFall *, + const DcalcAnalysisPt *) +{ + return nullptr; +} + +void +UnitDelayCalc::reduceParasitic(const Parasitic *, + const Net *, + const Corner *, + const MinMaxAll *) { - return ReducedParasiticType::none; } ArcDcalcResult diff --git a/dcalc/UnitDelayCalc.hh b/dcalc/UnitDelayCalc.hh index 788a3742..8d247f5d 100644 --- a/dcalc/UnitDelayCalc.hh +++ b/dcalc/UnitDelayCalc.hh @@ -29,7 +29,14 @@ public: Parasitic *findParasitic(const Pin *drvr_pin, const RiseFall *rf, const DcalcAnalysisPt *dcalc_ap) override; - ReducedParasiticType reducedParasiticType() const override; + Parasitic *reduceParasitic(const Parasitic *parasitic_network, + const Pin *drvr_pin, + const RiseFall *rf, + const DcalcAnalysisPt *dcalc_ap) override; + void reduceParasitic(const Parasitic *parasitic_network, + const Net *net, + const Corner *corner, + const MinMaxAll *min_max) override; ArcDcalcResult inputPortDelay(const Pin *port_pin, float in_slew, const RiseFall *rf, diff --git a/doc/ChangeLog.txt b/doc/ChangeLog.txt index d6a2e522..f665546c 100644 --- a/doc/ChangeLog.txt +++ b/doc/ChangeLog.txt @@ -10,6 +10,12 @@ The report_net -connections, -verbose and -hier_pins flags are deprecated. The report_instance -connections and -verbose flags are deprecated. The options are now enabled in all cases. +The read_spef parasitic reduction arguments have changed. The +-reduce_to and -delete_after_reduce arguments are deprecated and +replaced with the -reduce flag. With the -reduce flag, the current +delay calculator reduces the parastic network to the appropriate type +and deletes the parasitic network. + Release 2.4.0 2023/01/19 ------------------------- diff --git a/doc/OpenSTA.odt b/doc/OpenSTA.odt index 38c37ccbc4a853bf2eb845bfbcdc61ffa70d71c2..30afc942bfb3925e7e5bdbad91fe93567e9f1fce 100644 GIT binary patch delta 76137 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≥$#8akE;!We6 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%0AxYyD>$rno?C1}le#uGp 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