From bdd66c5a2c9dc74f120dd939e7215c3683d081ac Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Thu, 24 Sep 2026 09:45:27 -0700 Subject: [PATCH 01/52] dcalc multi-drvr do not use levels Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 121 +++++++++++++++++----------------- include/sta/GraphDelayCalc.hh | 16 +++-- 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 0783800b..fa4d66e2 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -165,11 +165,10 @@ void GraphDelayCalc::deleteMultiDrvrNets() { std::set drvr_nets; - for (auto [vertex, multi_drvr] : multi_drvr_net_map_) { + for (auto [vertex, multi_drvr] : multi_drvr_net_map_) // Multiple drvr pins point to the same drvr PinSet, // so collect them into a set. drvr_nets.insert(multi_drvr); - } multi_drvr_net_map_.clear(); deleteContents(drvr_nets); } @@ -255,7 +254,7 @@ GraphDelayCalc::delayInvalid(Vertex *vertex) // Invalidate driver that triggers dcalc for multi-driver nets. MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); if (multi_drvr) - invalid_delays_.insert(multi_drvr->dcalcDrvr()); + multi_drvr->setLoadSlewsInited(false); } } @@ -798,15 +797,14 @@ GraphDelayCalc::findDriverDelays(Vertex *drvr_vertex, LoadPinIndexMap &load_pin_index_map) { MultiDrvrNet *multi_drvr = findMultiDrvrNet(drvr_vertex); - if (multi_drvr == nullptr) { - initLoadSlews(drvr_vertex); - findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc, load_pin_index_map); + if (multi_drvr) { + if (!!multi_drvr->loadSlewsInited()) + initLoadSlews(drvr_vertex); + multi_drvr->setLoadSlewsInited(true); } - else if (drvr_vertex == multi_drvr->dcalcDrvr()) { + else initLoadSlews(drvr_vertex); - for (Vertex *drvr : multi_drvr->drvrs()) - findDriverDelays1(drvr, multi_drvr, arc_delay_calc, load_pin_index_map); - } + findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc, load_pin_index_map); arc_delay_calc->finishDrvrPin(); } @@ -882,8 +880,6 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) debugPrint(debug_, "delay_calc", 3, "multi-driver net"); MultiDrvrNet *multi_drvr = new MultiDrvrNet; VertexSeq &drvr_vertices = multi_drvr->drvrs(); - Level max_drvr_level = 0; - Vertex *max_drvr = nullptr; VertexInEdgeIterator edge_iter(load_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -895,16 +891,9 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) network_->pathName(drvr_pin)); multi_drvr_net_map_[drvr] = multi_drvr; drvr_vertices.push_back(drvr); - Level drvr_level = drvr->level(); - if (max_drvr == nullptr - || drvr_level > max_drvr_level) { - max_drvr = drvr; - max_drvr_level = drvr_level; - } } } } - multi_drvr->setDcalcDrvr(max_drvr); multi_drvr->findCaps(this); return multi_drvr; } @@ -1268,32 +1257,48 @@ GraphDelayCalc::annotateLoadDelays(Vertex *drvr_vertex, load_vertex->to_string(this), delayAsString(wire_delay, this), delayAsString(load_slew, this)); - if (!load_vertex->slewAnnotated(drvr_rf, min_max)) { - if (drvr_vertex->slewAnnotated(drvr_rf, min_max)) { - // Copy the driver slew to the load if it is annotated. - const Slew drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); - graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); - } - else { - const Slew slew = graph_->slew(load_vertex, drvr_rf, ap_index); - if (!merge - || delayGreater(load_slew, slew, min_max, this)) { - graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); - } - } - } - if (!graph_->wireDelayAnnotated(wire_edge, drvr_rf, ap_index)) { - // Multiple timing arcs with the same output transition - // annotate the same wire edges so they must be combined - // rather than set. - const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, ap_index); - Delay wire_delay_extra = delaySum(extra_delay, wire_delay, this); - if (!merge - || delayGreater(wire_delay_extra, delay, min_max, this)) { - graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay_extra); - if (observer_) - observer_->delayChangedTo(load_vertex); - } + Delay wire_delay_extra = delaySum(extra_delay, wire_delay, this); + annotateLoadDelaySlew(drvr_vertex, wire_edge, wire_delay_extra, + load_vertex, load_slew, + merge, drvr_rf, min_max, ap_index); + } + } +} + +void +GraphDelayCalc::annotateLoadDelaySlew(Vertex *drvr_vertex, + Edge *wire_edge, + const Delay &wire_delay, + Vertex *load_vertex, + const Slew &load_slew, + bool merge, + const RiseFall *drvr_rf, + const MinMax *min_max, + DcalcAPIndex ap_index) +{ + if (!graph_->wireDelayAnnotated(wire_edge, drvr_rf, ap_index)) { + // Multiple timing arcs with the same output transition + // annotate the same wire edges so they must be combined + // rather than set. + const ArcDelay &delay = graph_->wireArcDelay(wire_edge, drvr_rf, ap_index); + if (!merge + || delayGreater(wire_delay, delay, min_max, this)) { + graph_->setWireArcDelay(wire_edge, drvr_rf, ap_index, wire_delay); + if (observer_) + observer_->delayChangedTo(load_vertex); + } + } + if (!load_vertex->slewAnnotated(drvr_rf, min_max)) { + if (drvr_vertex->slewAnnotated(drvr_rf, min_max)) { + // Copy the driver slew to the load if it is annotated. + const Slew drvr_slew = graph_->slew(drvr_vertex, drvr_rf, ap_index); + graph_->setSlew(load_vertex, drvr_rf, ap_index, drvr_slew); + } + else { + const Slew slew = graph_->slew(load_vertex, drvr_rf, ap_index); + if (!merge + || delayGreater(load_slew, slew, min_max, this)) { + graph_->setSlew(load_vertex, drvr_rf, ap_index, load_slew); } } } @@ -1481,7 +1486,7 @@ GraphDelayCalc::initSlew(Vertex *vertex) for (Scene *scene : scenes_) { for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - for (const RiseFall *rf : RiseFall::range()) { + for (const RiseFall *rf : RiseFall::range()) { if (!vertex->slewAnnotated(rf, min_max)) graph_->setSlew(vertex, rf, ap_index, min_max->initValue()); } @@ -1500,17 +1505,15 @@ GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex, if (!drvr_vertex->slewAnnotated(rf, min_max)) graph_->setSlew(drvr_vertex, rf, ap_index, min_max->initValue()); - // Init wire delays and slews. + // Zero wire delays and slews. 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_); - if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) - graph_->setWireArcDelay(wire_edge, rf, ap_index, 0.0); - // Init load vertex slew. - if (!load_vertex->slewAnnotated(rf, min_max)) - graph_->setSlew(load_vertex, rf, ap_index, 0.0); + annotateLoadDelaySlew(drvr_vertex, wire_edge, delay_zero, + load_vertex, delay_zero, + true, rf, min_max, ap_index); } } } @@ -1783,7 +1786,7 @@ MultiDrvrNet::findCaps(const StaState *sta) { int count = RiseFall::index_count * sta->dcalcAnalysisPtCount(); net_caps_.resize(count); - const Pin *drvr_pin = dcalc_drvr_->pin(); + const Pin *drvr_pin = drvrs_[0]->pin(); for (Scene *scene : sta->scenes()) { const Sdc *sdc = scene->sdc(); for (const MinMax *min_max : MinMax::range()) { @@ -1803,16 +1806,16 @@ MultiDrvrNet::findCaps(const StaState *sta) } } -void -MultiDrvrNet::setDcalcDrvr(Vertex *drvr) +bool +MultiDrvrNet::parallelGates(const Network *network) const { - dcalc_drvr_ = drvr; + return network->direction(drvrs_[0]->pin())->isOutput(); } -bool -MultiDrvrNet::parallelGates(const Network *network) const +void +MultiDrvrNet::setLoadSlewsInited(bool inited) { - return network->direction(dcalc_drvr_->pin())->isOutput(); + load_slews_inited_ = inited; } } // namespace sta diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index e8dc919d..93b26871 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -259,6 +259,15 @@ protected: bool merge, const Scene *scene, const MinMax *min_max); + void annotateLoadDelaySlew(Vertex *drvr_vertex, + Edge *wire_edge, + const Delay &wire_delay, + Vertex *load_vertex, + const Slew &load_slew, + bool merge, + const RiseFall *drvr_rf, + const MinMax *min_max, + DcalcAPIndex ap_index); void findLatchEdgeDelays(Edge *edge); void findCheckEdgeDelays(Edge *edge, ArcDelayCalc *arc_delay_calc); @@ -336,8 +345,6 @@ public: VertexSeq &drvrs() { return drvrs_; } const VertexSeq &drvrs() const { return drvrs_; } bool parallelGates(const Network *network) const; - Vertex *dcalcDrvr() const { return dcalc_drvr_; } - void setDcalcDrvr(Vertex *drvr); void netCaps(const RiseFall *rf, const Scene *scene, const MinMax *min_max, @@ -347,13 +354,14 @@ public: float &fanout, bool &has_net_load) const; void findCaps(const StaState *sta); + bool loadSlewsInited() const { return load_slews_inited_; } + void setLoadSlewsInited(bool inited); private: - // Driver that triggers delay calculation for all the drivers on the net. - Vertex *dcalc_drvr_{nullptr}; VertexSeq drvrs_; // [drvr_rf->index][dcalc_ap->index] std::vector net_caps_; + bool load_slews_inited_{false}; }; } // namespace sta From 9ef4b4f032b53188ce5b13cd0c5190c11b62d5b4 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Fri, 25 Sep 2026 10:24:21 -0700 Subject: [PATCH 02/52] crpr do not use levels Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/Crpr.cc | 52 ++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/search/Crpr.cc b/search/Crpr.cc index 105f3a5c..b61380a6 100644 --- a/search/Crpr.cc +++ b/search/Crpr.cc @@ -208,38 +208,32 @@ CheckCrpr::findCrpr(const Path *src_clk_path, break; } } - const Path *src_clk_path2 = src_clk_path1; - const Path *tgt_clk_path2 = tgt_clk_path1; - // src_clk_path2 and tgt_clk_path2 are now in the same (gen)clk src path. - // Use the vertex levels to back up the deeper path to see if they - // overlap. - int src_level = src_clk_path2->vertex(this)->level(); - int tgt_level = tgt_clk_path2->vertex(this)->level(); - while (src_clk_path2->pin(this) != tgt_clk_path2->pin(this)) { - int level_diff = src_level - tgt_level; - if (level_diff >= 0) { - src_clk_path2 = src_clk_path2->prevPath(); - if (src_clk_path2 == nullptr - || src_clk_path2->isNull()) - break; - src_level = src_clk_path2->vertex(this)->level(); - } - if (level_diff <= 0) { - tgt_clk_path2 = tgt_clk_path2->prevPath(); - if (tgt_clk_path2 == nullptr - || tgt_clk_path2->isNull()) - break; - tgt_level = tgt_clk_path2->vertex(this)->level(); + std::map src_pin_map; + const Path *src = src_clk_path1; + while (src && !src->isNull()) { + src_pin_map[src->pin(this)] = src; + src = src->prevPath(); + } + + const Path *tgt = tgt_clk_path1; + src = nullptr; + Pin *tgt_pin = nullptr; + while (tgt && !tgt->isNull()) { + tgt_pin = tgt->pin(this); + auto src_itr = src_pin_map.find(tgt_pin); + if (src_itr != src_pin_map.end()) { + src = src_itr->second; + break; } + tgt = tgt->prevPath(); } - if (src_clk_path2 && !src_clk_path2->isNull() - && tgt_clk_path2 && !tgt_clk_path2->isNull() - && (src_clk_path2->transition(this) == tgt_clk_path2->transition(this) - || same_pin)) { + if (src + && (same_pin + || src->transition(this) == tgt->transition(this))) { debugPrint(debug_, "crpr", 2, "crpr pin {}", - network_->pathName(src_clk_path2->pin(this))); - crpr = findCrpr1(src_clk_path2, tgt_clk_path2); - crpr_pin = src_clk_path2->pin(this); + network_->pathName(tgt_pin)); + crpr = findCrpr1(src, tgt); + crpr_pin = tgt_pin; } } From f1237f7942015ecc82c320ff4d48ce0e1f33ad87 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Fri, 25 Sep 2026 12:58:59 -0700 Subject: [PATCH 03/52] comment Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- test/input_delay_ref_pin_rebuild.tcl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/input_delay_ref_pin_rebuild.tcl b/test/input_delay_ref_pin_rebuild.tcl index dcf0ac14..bbac3c70 100644 --- a/test/input_delay_ref_pin_rebuild.tcl +++ b/test/input_delay_ref_pin_rebuild.tcl @@ -1,10 +1,5 @@ -# set_input_delay -reference_pin must survive a graph rebuild that keeps SDC -# (resizer-like: sta::network_changed_non_sdc deletes the graph but not the -# constraints). The ref_pin->input graph edge is owned by the graph; its -# existence flag (PortDelay::ref_pin_edges_exist_) must be reset on graph -# teardown so ensureInputDelayRefPinEdges() rebuilds the edge. Otherwise in1 -# loses its arrival seeding and reports "No paths found" after the rebuild. -# in2 is a plain input delay (no ref_pin) used as a control. +# set_input_delay -reference_pin / sta::network_changed_non_sdc +# OpenROAD resizer calls sta::network_changed_non_sdc to delete the graph but not SDC. read_liberty asap7_small.lib.gz read_verilog reg1_asap7.v link_design top From e3326b03dbb7f88e362d574c3e7fff071fc36eac Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sat, 26 Sep 2026 16:53:06 -0700 Subject: [PATCH 04/52] GraphDelayCalc::findDelays() Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 6 ++++++ include/sta/GraphDelayCalc.hh | 1 + search/Sta.cc | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index fa4d66e2..50d3793b 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -333,6 +333,12 @@ FindVertexDelays::visit(Vertex *vertex) graph_delay_calc_->findVertexDelay(vertex, arc_delay_calc_); } +void +GraphDelayCalc::findDelays() +{ + return findDelays(Graph::vertex_level_max); +} + // The logical structure of incremental delay calculation closely // resembles the incremental search arrival time algorithm // (Search::findArrivals). diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 93b26871..4bd34ad5 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -67,6 +67,7 @@ public: virtual void levelChangedBefore(Vertex *vertex); // Reset to virgin state. virtual void clear(); + void findDelays(); // Find arc delays and vertex slews thru level. virtual void findDelays(Level level); // Find and annotate drvr_vertex gate and load delays/slews. diff --git a/search/Sta.cc b/search/Sta.cc index fdac988e..a1b3a226 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -3692,7 +3692,7 @@ void Sta::findDelays() { delayCalcPreamble(); - graph_delay_calc_->findDelays(levelize_->maxLevel()); + graph_delay_calc_->findDelays(); } void From 4654362dd2b3ad7ebaf03e07d05f44450811c134 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:24:04 -0700 Subject: [PATCH 05/52] Levelize::ensureRoots() Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/Levelize.cc | 80 ++++++++++++++++++++++++---------------------- search/Levelize.hh | 4 +-- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/search/Levelize.cc b/search/Levelize.cc index a6738ed1..7b228b99 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -105,7 +105,7 @@ Levelize::ensureLevelized() } } -#define onPath() visied2() +#define onPath() visited2() #define setOnPath(on_path) setVisited2(on_path) void @@ -117,28 +117,14 @@ Levelize::findLevels() if (observer_) observer_->levelsChangedBefore(); - for (const Mode *mode : modes_) - mode->sdc()->ensureInputDelayRefPinEdges(); - - VertexIterator vertex_iter(graph_); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - // findBackEdges() init - vertex->setVisited(false); - vertex->setOnPath(false); - // assignLevels init - vertex->setLevel(-1); - } - - findRoots(); findBackEdges(); VertexSeq topo_sorted = findTopologicalOrder(); assignLevels(topo_sorted); // Set level of stranded vertices (constants) to zero. - VertexIterator vertex_iter2(graph_); - while (vertex_iter2.hasNext()) { - Vertex *vertex = vertex_iter2.next(); + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); if (vertex->level() == -1) setLevel(vertex, 0); // cleanup @@ -152,26 +138,31 @@ Levelize::findLevels() } void -Levelize::findRoots() +Levelize::ensureRoots() { - roots_.clear(); - VertexIterator vertex_iter(graph_); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - if (isRoot(vertex)) { - debugPrint(debug_, "levelize", 2, "root {}{}", vertex->to_string(this), - hasFanout(vertex) ? " fanout" : ""); - roots_.insert(vertex); + if (roots_.empty()) { + for (const Mode *mode : modes_) + mode->sdc()->ensureInputDelayRefPinEdges(); + + roots_.clear(); + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (isRoot(vertex)) { + debugPrint(debug_, "levelize", 2, "root {}{}", vertex->to_string(this), + hasFanout(vertex) ? " fanout" : ""); + roots_.insert(vertex); + } } - } - if (debug_->check("levelize", 1)) { - size_t fanout_roots = 0; - for (Vertex *root : roots_) { - if (hasFanout(root)) - fanout_roots++; + if (debug_->check("levelize", 1)) { + size_t fanout_roots = 0; + for (Vertex *root : roots_) { + if (hasFanout(root)) + fanout_roots++; + } + debugPrint(debug_, "levelize", 1, "Found {} roots {} with fanout", + roots_.size(), fanout_roots); } - debugPrint(debug_, "levelize", 1, "Found {} roots {} with fanout", - roots_.size(), fanout_roots); } } @@ -226,11 +217,22 @@ void Levelize::findBackEdges() { Stats stats(debug_, report_); + + ensureRoots(); + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + // findBackEdges() init + vertex->setVisited(false); + vertex->setOnPath(false); + // assignLevels init + vertex->setLevel(-1); + } + EdgeSeq path; FindBackEdgesStack stack; - - VertexSeq sorted_roots = sortedRootsWithFanout(); - for (Vertex *vertex : sorted_roots) { + VertexSeq roots = sortedRootsWithFanout(); + for (Vertex *vertex : roots_) { vertex->setVisited(true); vertex->setOnPath(true); stack.emplace(vertex, new VertexOutEdgeIterator(vertex, graph_)); @@ -274,7 +276,7 @@ Levelize::findBackEdges(EdgeSeq &path, path.push_back(edge); stack.emplace(to_vertex, new VertexOutEdgeIterator(to_vertex, graph_)); } - else if (to_vertex->visited2()) { // on path + else if (to_vertex->onPath()) { // Found a back edge (loop). recordLoop(edge, path); back_edges.insert(edge); diff --git a/search/Levelize.hh b/search/Levelize.hh index 22b268b8..aedd9991 100644 --- a/search/Levelize.hh +++ b/search/Levelize.hh @@ -58,7 +58,8 @@ public: void deleteEdgeBefore(Edge *edge); int maxLevel() const { return max_level_; } // Vertices with no fanin edges. - VertexSet &roots() { return roots_; } + void ensureRoots(); + const VertexSet &roots() const { return roots_; } bool isRoot(Vertex *vertex); bool hasFanout(Vertex *vertex); bool searchThru(Edge *edge); @@ -75,7 +76,6 @@ public: void findLevels(); protected: - void findRoots(); VertexSeq sortedRootsWithFanout(); VertexSeq findTopologicalOrder(); void assignLevels(VertexSeq &topo_sorted); From 9010436bc23047e1bf8f76fdf968b68afbc65ac7 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sun, 27 Sep 2026 15:42:29 -0700 Subject: [PATCH 06/52] Levelize::ensureBackEdges() Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/Levelize.cc | 26 ++++++++++++++++++-------- search/Levelize.hh | 4 +++- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/search/Levelize.cc b/search/Levelize.cc index 7b228b99..926a1240 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -76,14 +76,10 @@ Levelize::clear() { levelized_ = false; levels_valid_ = false; + max_level_ = 0; roots_.clear(); relevelize_from_.clear(); clearLoopEdges(); - for (auto loop : loops_) - delete loop; - loops_.clear(); - loop_edges_.clear(); - max_level_ = 0; } void @@ -92,6 +88,12 @@ Levelize::clearLoopEdges() for (Edge *edge : disabled_loop_edges_) edge->setIsDisabledLoop(false); disabled_loop_edges_.clear(); + + for (auto loop : loops_) + delete loop; + loops_.clear(); + loop_edges_.clear(); + back_edges_valid_ = false; } void @@ -117,7 +119,7 @@ Levelize::findLevels() if (observer_) observer_->levelsChangedBefore(); - findBackEdges(); + ensureBackEdges(); VertexSeq topo_sorted = findTopologicalOrder(); assignLevels(topo_sorted); @@ -212,13 +214,21 @@ Levelize::hasFanout(Vertex *vertex) return has_fanout; } +void +Levelize::ensureBackEdges() +{ + if (!back_edges_valid_) { + ensureRoots(); + findBackEdges(); + back_edges_valid_ = true; + } +} + // Non-recursive DFS to find back edges so the graph is acyclic. void Levelize::findBackEdges() { Stats stats(debug_, report_); - - ensureRoots(); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); diff --git a/search/Levelize.hh b/search/Levelize.hh index aedd9991..f3864d94 100644 --- a/search/Levelize.hh +++ b/search/Levelize.hh @@ -65,6 +65,7 @@ public: bool searchThru(Edge *edge); // Reset to virgin state. void clear(); + void ensureBackEdges(); // Edge is disabled to break combinational loops. bool isDisabledLoop(Edge *edge) const; // Only valid when levels are valid. @@ -79,11 +80,11 @@ protected: VertexSeq sortedRootsWithFanout(); VertexSeq findTopologicalOrder(); void assignLevels(VertexSeq &topo_sorted); + void findBackEdges(); void recordLoop(Edge *edge, EdgeSeq &path); EdgeSeq *loopEdges(EdgeSeq &path, Edge *closing_edge); - void findBackEdges(); EdgeSet findBackEdges(EdgeSeq &path, FindBackEdgesStack &stack); void findCycleBackEdges(); @@ -109,6 +110,7 @@ protected: Level level_space_{10}; VertexSet roots_; VertexSet relevelize_from_; + bool back_edges_valid_{false}; GraphLoopSeq loops_; EdgeSet loop_edges_; EdgeSet disabled_loop_edges_; From 0eaeba09c7d762222095da6e99141d924d69c287 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sun, 27 Sep 2026 15:55:19 -0700 Subject: [PATCH 07/52] GraphDelayCalc unvirutal Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- include/sta/GraphDelayCalc.hh | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 4bd34ad5..94302310 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -56,32 +56,32 @@ public: ~GraphDelayCalc() override; void copyState(const StaState *sta) override; // Set the observer for edge delay changes. - virtual void setObserver(DelayCalcObserver *observer); + void setObserver(DelayCalcObserver *observer); // Invalidate all delays/slews. - virtual void delaysInvalid(); - virtual void levelsChangedBefore(); + void delaysInvalid(); + void levelsChangedBefore(); // Invalidate vertex and downstream delays/slews. - virtual void delayInvalid(Vertex *vertex); - virtual void delayInvalid(const Pin *pin); - virtual void deleteVertexBefore(Vertex *vertex); - virtual void levelChangedBefore(Vertex *vertex); + void delayInvalid(Vertex *vertex); + void delayInvalid(const Pin *pin); + void deleteVertexBefore(Vertex *vertex); + void levelChangedBefore(Vertex *vertex); // Reset to virgin state. - virtual void clear(); + void clear(); void findDelays(); // Find arc delays and vertex slews thru level. - virtual void findDelays(Level level); + void findDelays(Level level); // Find and annotate drvr_vertex gate and load delays/slews. - virtual void findDelays(Vertex *drvr_vertex); + void findDelays(Vertex *drvr_vertex); // Returned string is owned by the caller. - virtual std::string reportDelayCalc(const Edge *edge, - const TimingArc *arc, - const Scene *scene, - const MinMax *min_max, - int digits); + std::string reportDelayCalc(const Edge *edge, + const TimingArc *arc, + const Scene *scene, + const MinMax *min_max, + int digits); // Percentage (0.0:1.0) change in delay that causes downstream // delays to be recomputed during incremental delay calculation. - virtual float incrementalDelayTolerance(); - virtual void setIncrementalDelayTolerance(float tol); + float incrementalDelayTolerance(); + void setIncrementalDelayTolerance(float tol); float loadCap(const Pin *drvr_pin, const Scene *scene, From fe1f931c67ccdf0f1b15fad936e38d8082c8971d Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Mon, 28 Sep 2026 08:50:30 -0700 Subject: [PATCH 08/52] write_timing_model: port max_transition/max_capacitance #518 Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/MakeTimingModel.cc | 47 ++++++++++++++++++++++++++++++++++++ search/MakeTimingModelPvt.hh | 2 ++ 2 files changed, 49 insertions(+) diff --git a/search/MakeTimingModel.cc b/search/MakeTimingModel.cc index 0ab2d9d8..a505dcfd 100644 --- a/search/MakeTimingModel.cc +++ b/search/MakeTimingModel.cc @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -217,6 +218,7 @@ MakeTimingModel::makePorts() LibertyPort *lib_bit_port = modelPort(pin); float load_cap = graph_delay_calc_->loadCap(pin, scene_, min_max_); lib_bit_port->setCapacitance(load_cap); + setPortLimits(pin, lib_bit_port); } delete member_iter; } @@ -226,11 +228,56 @@ MakeTimingModel::makePorts() Pin *pin = network_->findPin(top_inst, port); float load_cap = graph_delay_calc_->loadCap(pin, scene_, min_max_); lib_port->setCapacitance(load_cap); + setPortLimits(pin, lib_port); } } delete port_iter; } +// Find port max_cap/max_slew limits. +void +MakeTimingModel::setPortLimits(const Pin *pin, + LibertyPort *lib_port) +{ + const PortDirection *dir = network_->direction(pin); + float slew_limit = std::numeric_limits::max(); + float cap_limit = std::numeric_limits::max(); + bool slew_exists = false; + bool cap_exists = false; + PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(pin); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + const LibertyPort *port = network_->libertyPort(pin); + if (port) { + const LibertyLibrary *lib = port->libertyCell()->libertyLibrary(); + float limit; + bool exists; + port->slewLimit(min_max_, limit, exists); + if (!exists) + lib->defaultMaxSlew(limit, exists); + if (exists && limit < slew_limit) { + slew_limit = limit; + slew_exists = true; + } + + if (dir->isAnyOutput()) { + port->capacitanceLimit(min_max_, limit, exists); + if (!exists) + lib->defaultMaxCapacitance(limit, exists); + if (exists && limit < cap_limit) { + cap_limit = limit; + cap_exists = true; + } + } + } + } + delete pin_iter; + if (slew_exists) + lib_port->setSlewLimit(slew_limit, min_max_); + if (cap_exists) + lib_port->setCapacitanceLimit(cap_limit, min_max_); +} + void MakeTimingModel::checkClock(Clock *clk) { diff --git a/search/MakeTimingModelPvt.hh b/search/MakeTimingModelPvt.hh index 47dba148..8a6361de 100644 --- a/search/MakeTimingModelPvt.hh +++ b/search/MakeTimingModelPvt.hh @@ -72,6 +72,8 @@ private: void makeCell(); float findArea(); void makePorts(); + void setPortLimits(const Pin *pin, + LibertyPort *lib_port); void checkClock(Clock *clk); void findTimingFromInputs(); void findTimingFromInput(Port *input_port); From 1b066c3fef82a4d8fdcf103343d86dd7d4ca4cd6 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:21:28 -0700 Subject: [PATCH 09/52] $errorInfo traceback #516 Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- tcl/Init.tcl | 18 ++++++++ tcl/Util.tcl | 69 +++++++++++++++++++++++++----- test/write_path_spice_arc_sense.ok | 2 +- 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/tcl/Init.tcl b/tcl/Init.tcl index 93632119..b13f4236 100644 --- a/tcl/Init.tcl +++ b/tcl/Init.tcl @@ -22,6 +22,16 @@ # # This notice may not be removed or altered from any source distribution. +# Trim tclreadline::Loop's eval frames from the end of $errorInfo. +proc ::sta::trim_tclreadline_error_info { args } { + global errorInfo + set marker "\n (\"eval\" body line " + set idx [string last $marker $errorInfo] + if { $idx != -1 } { + set errorInfo [string range $errorInfo 0 [expr { $idx - 1 }]] + } +} + proc init_sta_cmds {} { global auto_index @@ -36,5 +46,13 @@ proc init_sta_cmds {} { ::tclreadline::readline customcompleter ::tclreadline::ScriptCompleter proc ::tclreadline::prompt1 {} { return {% } } proc ::tclreadline::prompt2 {} { return {> } } + # tclreadline::Setup does catch {rename ::tclreadline::Exit ""}. + # If Exit does not exist that catch fails and overwrites $errorInfo, + # hiding any traceback from errors in the .sta init file. + proc ::tclreadline::Exit { args } {} + # Drop the interactive Loop's "eval $::tclreadline::LINE" frames + # that otherwise trail every command's $errorInfo. + trace add variable ::tclreadline::errorMsg write \ + ::sta::trim_tclreadline_error_info } } diff --git a/tcl/Util.tcl b/tcl/Util.tcl index f0cc1d83..369a50ef 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -392,7 +392,7 @@ proc sdc_filename {} { } proc sdc_file_line { } { - variable include_line + variable cmd_start_line for { set fr [info frame] } { $fr >= 0 } { incr fr -1 } { set type [dict get [info frame $fr] type] if { $type == "source" } { @@ -400,7 +400,7 @@ proc sdc_file_line { } { } if { $type == "proc" \ && [lindex [dict get [info frame $fr] cmd] 0] == "include_file" } { - return $include_line + return $cmd_start_line } } return 1 @@ -584,21 +584,46 @@ proc_redirect include { set echo [expr [info exists flags(-echo)] || [info exists flags(-e)]] set verbose [expr [info exists flags(-verbose)] || [info exists flags(-v)]] set filename [file nativename [lindex $args 0]] - include_file $filename $echo $verbose + # Re-raise with the included command's traceback so $errorInfo is not + # dominated by include_file's own stack frames. Use error (not + # return -code error) so proc_redirect's catch sees code 1. + if { [catch {include_file $filename $echo $verbose} result] } { + variable include_error_info + if { [info exists include_error_info] } { + set traceback $include_error_info + unset include_error_info + error $result $traceback + } + error $result $::errorInfo + } +} + +# Drop the uplevel frames include_file itself adds to the end of a traceback. +proc trim_traceback { traceback } { + set last [string last "\n (\"uplevel\" body line " $traceback] + if { $last != -1 } { + return [string range $traceback 0 [expr { $last - 1 }]] + } + return $traceback } proc include_file { filename echo verbose } { global sta_continue_on_error variable include_line - + variable cmd_start_line + set prev_filename [info script] if { [info exists include_line] } { set prev_line $include_line } + if { [info exists cmd_start_line] } { + set prev_cmd_start_line $cmd_start_line + } try { # set filename/line for sta_warn/error info script $filename set include_line 1 + set cmd_start_line 1 if [catch {open $filename r} stream] { sta_error 340 "cannot open '$filename'." } else { @@ -610,6 +635,7 @@ proc include_file { filename echo verbose } { } set cmd "" set error {} + set error_traceback {} while {![eof $stream]} { gets $stream line if { $line != "" } { @@ -617,18 +643,26 @@ proc include_file { filename echo verbose } { report_line $line } } + if { $cmd == "" } { + set cmd_start_line $include_line + } append cmd $line "\n" if { [string index $line end] != "\\" \ && [info complete $cmd] } { set error {} - set error_code [catch {uplevel \#0 $cmd} result] + set error_traceback {} + set error_code [catch {uplevel \#0 $cmd} result error_options] # cmd consumed set cmd "" # Flush results printed outside tcl to stdout/stderr. fflush switch $error_code { 0 { if { $verbose && $result != "" } { report_line $result } } - 1 { set error $result } + 1 { + set error $result + # -errorinfo is the error message followed by the traceback. + set error_traceback [trim_traceback [dict get $error_options -errorinfo]] + } 2 { set error {invoked "return" outside of a proc.} } 3 { set error {invoked "break" outside of a loop.} } 4 { set error {invoked "continue" outside of a loop.} } @@ -639,9 +673,10 @@ proc include_file { filename echo verbose } { if { [string first "Error" $error] == 0 } { report_line $error } else { - report_line "Error: [file tail $filename], $include_line $error" + report_line "Error: [file tail $filename], $cmd_start_line $error" } set error {} + set error_traceback {} } else { break } @@ -651,14 +686,21 @@ proc include_file { filename echo verbose } { } close $stream if { $cmd != {} } { - sta_error 341 "incomplete command at end of file." + sta_error 341 "incomplete command at end of file, starting on line $cmd_start_line." } if { $error != {} } { # Only prepend error message with file/line once. - if { [string first "Error" $error] == 0 } { - error $error + # Pass the failing command's traceback so puts $errorInfo shows + # the call stack inside the included file. + if { [string first "Error" $error] != 0 } { + set error "Error: [file tail $filename], $include_line $error" + } + if { $error_traceback != {} } { + variable include_error_info + set include_error_info $error_traceback + error $error $error_traceback } else { - error "Error: [file tail $filename], $include_line $error" + error $error } } } @@ -671,6 +713,11 @@ proc include_file { filename echo verbose } { } else { unset include_line } + if { [info exists prev_cmd_start_line] } { + set cmd_start_line $prev_cmd_start_line + } else { + unset -nocomplain cmd_start_line + } } } diff --git a/test/write_path_spice_arc_sense.ok b/test/write_path_spice_arc_sense.ok index 520c5ad5..18554a33 100644 --- a/test/write_path_spice_arc_sense.ok +++ b/test/write_path_spice_arc_sense.ok @@ -1,5 +1,5 @@ Warning 1171: write_path_spice_arc_sense.lib.gz line 23, default_fanout_load is 0.0. -Warning 1927: write_path_spice_arc_sense.tcl line 18, No -model_file specified. +Warning 1927: write_path_spice_arc_sense.tcl line 14, No -model_file specified. * Path from a v to x ^ .include "write_path_spice_arc_sense.sp_1.subckt" .tran 1e-13 3.33e-09 From 5adf472606d5c8423094b537fba8fcd2d71821e0 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Tue, 29 Sep 2026 21:19:06 -0700 Subject: [PATCH 10/52] GraphDelayCalc::findDelays bfs iter ensureSize #519 Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 50d3793b..97a9c138 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -680,6 +680,7 @@ GraphDelayCalc::findInputArcDelay(const Pin *drvr_pin, void GraphDelayCalc::findDelays(Vertex *drvr_vertex) { + iter_->ensureSize(); findVertexDelay(drvr_vertex, arc_delay_calc_); } From f3cd9b1efac597de4810833ba5b2f49b7bfb083b Mon Sep 17 00:00:00 2001 From: Deepashree Sengupta Date: Wed, 30 Sep 2026 15:56:19 -0400 Subject: [PATCH 11/52] PortDirection: replace heap-allocated singletons with static objects (#521) * fix for issue 389 Signed-off-by: dsengupta0628 * use libs from test/ dir not examples/ Signed-off-by: dsengupta0628 * Remove port_direction_reinit test initSta() is intended to be called once per process; the test exercised unsupported re-initialization. * Remove PortDirection::init/destroy Singletons are static objects; nothing to allocate or free. --------- Signed-off-by: dsengupta0628 --- include/sta/PortDirection.hh | 64 +++++++++++------------ network/PortDirection.cc | 98 ++++++++++-------------------------- search/Sta.cc | 2 - 3 files changed, 60 insertions(+), 104 deletions(-) diff --git a/include/sta/PortDirection.hh b/include/sta/PortDirection.hh index 72d4f074..8fc9e454 100644 --- a/include/sta/PortDirection.hh +++ b/include/sta/PortDirection.hh @@ -31,55 +31,57 @@ namespace sta { class PortDirection { public: - static void init(); - static void destroy(); // Singleton accessors. - static PortDirection *input() { return input_; } - static PortDirection *output() { return output_; } - static PortDirection *tristate() { return tristate_; } - static PortDirection *bidirect() { return bidirect_; } - static PortDirection *internal() { return internal_; } - static PortDirection *ground() { return ground_; } - static PortDirection *power() { return power_; } - static PortDirection *well() { return well_; } - static PortDirection *unknown() { return unknown_; } + static PortDirection *input() { return &input_; } + static PortDirection *output() { return &output_; } + static PortDirection *tristate() { return &tristate_; } + static PortDirection *bidirect() { return &bidirect_; } + static PortDirection *internal() { return &internal_; } + static PortDirection *ground() { return &ground_; } + static PortDirection *power() { return &power_; } + static PortDirection *well() { return &well_; } + static PortDirection *unknown() { return &unknown_; } static PortDirection *find(const char *dir_name); std::string_view name() const { return name_; } size_t index() const { return index_; } - bool isInput() const { return this == input_; } + bool isInput() const { return this == &input_; } // Input or bidirect. bool isAnyInput() const; - bool isOutput() const { return this == output_; } + bool isOutput() const { return this == &output_; } // Output, tristate or bidirect. bool isAnyOutput() const; - bool isTristate() const { return this == tristate_; } - bool isBidirect() const { return this == bidirect_; } + bool isTristate() const { return this == &tristate_; } + bool isBidirect() const { return this == &bidirect_; } // Bidirect or tristate. bool isAnyTristate() const; - bool isGround() const { return this == ground_; } - bool isPower() const { return this == power_; } - bool isWell() const { return this == well_; } + bool isGround() const { return this == &ground_; } + bool isPower() const { return this == &power_; } + bool isWell() const { return this == &well_; } // Ground, power, or well. bool isPowerGround() const; - bool isInternal() const { return this == internal_; } - bool isUnknown() const { return this == unknown_; } + bool isInternal() const { return this == &internal_; } + bool isUnknown() const { return this == &unknown_; } private: - PortDirection(const char *name, - size_t index); + constexpr PortDirection(const char *name, + size_t index) : + name_(name), + index_(index) + { + } const char *name_; size_t index_; - static PortDirection *input_; - static PortDirection *output_; - static PortDirection *tristate_; - static PortDirection *bidirect_; - static PortDirection *internal_; - static PortDirection *ground_; - static PortDirection *power_; - static PortDirection *well_; - static PortDirection *unknown_; + static PortDirection input_; + static PortDirection output_; + static PortDirection tristate_; + static PortDirection bidirect_; + static PortDirection internal_; + static PortDirection ground_; + static PortDirection power_; + static PortDirection well_; + static PortDirection unknown_; }; } // namespace sta diff --git a/network/PortDirection.cc b/network/PortDirection.cc index 90df7a35..18cdc936 100644 --- a/network/PortDirection.cc +++ b/network/PortDirection.cc @@ -28,79 +28,35 @@ namespace sta { -PortDirection *PortDirection::input_; -PortDirection *PortDirection::output_; -PortDirection *PortDirection::tristate_; -PortDirection *PortDirection::bidirect_; -PortDirection *PortDirection::internal_; -PortDirection *PortDirection::ground_; -PortDirection *PortDirection::power_; -PortDirection *PortDirection::well_; -PortDirection *PortDirection::unknown_; - -void -PortDirection::init() -{ - input_ = new PortDirection("input", 0); - output_ = new PortDirection("output", 1); - tristate_ = new PortDirection("tristate", 2); - bidirect_ = new PortDirection("bidirect", 3); - internal_ = new PortDirection("internal", 4); - ground_ = new PortDirection("ground", 5); - power_ = new PortDirection("power", 6); - well_ = new PortDirection("well", 7); - unknown_ = new PortDirection("unknown", 8); -} - -void -PortDirection::destroy() -{ - delete input_; - input_ = nullptr; - delete output_; - output_ = nullptr; - delete tristate_; - tristate_ = nullptr; - delete bidirect_; - bidirect_ = nullptr; - delete internal_; - internal_ = nullptr; - delete ground_; - ground_ = nullptr; - delete power_; - power_ = nullptr; - delete well_; - well_ = nullptr; - delete unknown_; - unknown_ = nullptr; -} - -PortDirection::PortDirection(const char *name, - size_t index) : - name_(name), - index_(index) -{ -} +PortDirection PortDirection::input_("input", 0); +PortDirection PortDirection::output_("output", 1); +PortDirection PortDirection::tristate_("tristate", 2); +PortDirection PortDirection::bidirect_("bidirect", 3); +PortDirection PortDirection::internal_("internal", 4); +PortDirection PortDirection::ground_("ground", 5); +PortDirection PortDirection::power_("power", 6); +PortDirection PortDirection::well_("well", 7); +PortDirection PortDirection::unknown_("unknown", 8); PortDirection * PortDirection::find(const char *dir_name) { if (stringEqual(dir_name, "input")) - return input_; + return &input_; else if (stringEqual(dir_name, "output")) - return output_; + return &output_; else if (stringEqual(dir_name, "tristate")) - return tristate_; + return &tristate_; else if (stringEqual(dir_name, "bidirect")) - return bidirect_; + return &bidirect_; else if (stringEqual(dir_name, "internal")) - return internal_; + return &internal_; else if (stringEqual(dir_name, "ground")) - return ground_; + return &ground_; else if (stringEqual(dir_name, "power")) - return power_; + return &power_; else if (stringEqual(dir_name, "well")) - return well_; + return &well_; else return nullptr; } @@ -108,31 +64,31 @@ PortDirection::find(const char *dir_name) bool PortDirection::isAnyInput() const { - return this == input_ - || this == bidirect_; + return this == &input_ + || this == &bidirect_; } bool PortDirection::isAnyOutput() const { - return this == output_ - || this == tristate_ - || this == bidirect_; + return this == &output_ + || this == &tristate_ + || this == &bidirect_; } bool PortDirection::isAnyTristate() const { - return this == tristate_ - || this == bidirect_; + return this == &tristate_ + || this == &bidirect_; } bool PortDirection::isPowerGround() const { - return this == ground_ - || this == power_ - || this == well_; + return this == &ground_ + || this == &power_ + || this == &well_; } } // namespace sta diff --git a/search/Sta.cc b/search/Sta.cc index a1b3a226..e70bc872 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -247,7 +247,6 @@ void initSta() { initElapsedTime(); - PortDirection::init(); initLiberty(); initDelayConstants(); registerDelayCalcs(); @@ -263,7 +262,6 @@ deleteAllMemory() Sta::setSta(nullptr); } deleteDelayCalcs(); - PortDirection::destroy(); deleteLiberty(); } From 0e19cc06095fb5e11b7f722dd610aee7a5848e9b Mon Sep 17 00:00:00 2001 From: Deepashree Sengupta Date: Wed, 30 Sep 2026 15:57:18 -0400 Subject: [PATCH 12/52] Report the tcl traceback for an error while reading a file (#516) * Report the tcl traceback for an error while reading a file include_file reads a file one complete command at a time and evaluates each with catch, so the errorInfo tcl builds for a command that fails is only reachable there. It is discarded, and the error is reported as a single line naming the file and line but not the procs that raised it. sta_error_traceback, 0 by default, keeps it. catch is given its options dict and -errorinfo is used in place of the bare result; since errorInfo begins with the error message, the existing check that prepends the file and line only once is unaffected, and both the sta_continue_on_error report and the raise at the end of the file pick it up. The uplevel frames include_file itself adds are trimmed from the end so the traceback ends at the command read from the file. With the variable at 0 the reported text is unchanged. Signed-off-by: dsengupta0628 * Report the start line of an incomplete command Signed-off-by: dsengupta0628 * Report the start line of a multi-line command that failed include_line counts lines as they are read, so by the time a command spanning several lines runs it names the command's last line. An error inside an if that opens on line 3 and closes on line 7 was reported as line 7, and a warning from a command continued across lines 14 to 18 as line 18. cmd_start_line holds the first line of the command being accumulated. include_file reports it, and sdc_file_line returns it so the messages raised with a msg id while reading a file use it as well. Single line commands are unaffected. write_path_spice_arc_sense.ok is the one golden whose warning moves, from the last line of the command to the line the command starts on. Signed-off-by: dsengupta0628 * Report a multi-line command error at its first line When reading stops at the first error, include_file built the message with include_line, the last line of the failing command. Use cmd_start_line, as the continue-on-error message and sdc_file_line do. Remove the sta_error_traceback docs and test, superseded by the $errorInfo traceback in 1b066c3f. Signed-off-by: dsengupta0628 * Clear include_error_info at the start of include_file include_file saves the failing command's traceback in include_error_info, but only include clears it. read_sdc calls include_file directly, so after a failed read_sdc the traceback was left behind and reused by a later include error with no traceback of its own (e.g. cannot open the file). Signed-off-by: dsengupta0628 --------- Signed-off-by: dsengupta0628 --- tcl/Util.tcl | 5 +++-- test/include_error_line.ok | 2 ++ test/include_error_line.sdc | 3 +++ test/include_error_line.tcl | 7 +++++++ test/regression_vars.tcl | 1 + 5 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 test/include_error_line.ok create mode 100644 test/include_error_line.sdc create mode 100644 test/include_error_line.tcl diff --git a/tcl/Util.tcl b/tcl/Util.tcl index 369a50ef..8b42d48e 100644 --- a/tcl/Util.tcl +++ b/tcl/Util.tcl @@ -611,7 +611,9 @@ proc include_file { filename echo verbose } { global sta_continue_on_error variable include_line variable cmd_start_line + variable include_error_info + unset -nocomplain include_error_info set prev_filename [info script] if { [info exists include_line] } { set prev_line $include_line @@ -693,10 +695,9 @@ proc include_file { filename echo verbose } { # Pass the failing command's traceback so puts $errorInfo shows # the call stack inside the included file. if { [string first "Error" $error] != 0 } { - set error "Error: [file tail $filename], $include_line $error" + set error "Error: [file tail $filename], $cmd_start_line $error" } if { $error_traceback != {} } { - variable include_error_info set include_error_info $error_traceback error $error $error_traceback } else { diff --git a/test/include_error_line.ok b/test/include_error_line.ok new file mode 100644 index 00000000..7c43277d --- /dev/null +++ b/test/include_error_line.ok @@ -0,0 +1,2 @@ +Error: include_error_line.sdc, 1 invalid command name "include_error_line_undefined_cmd" +-1 diff --git a/test/include_error_line.sdc b/test/include_error_line.sdc new file mode 100644 index 00000000..1d5f3be1 --- /dev/null +++ b/test/include_error_line.sdc @@ -0,0 +1,3 @@ +if { 1 } { + include_error_line_undefined_cmd +} diff --git a/test/include_error_line.tcl b/test/include_error_line.tcl new file mode 100644 index 00000000..992b8e3a --- /dev/null +++ b/test/include_error_line.tcl @@ -0,0 +1,7 @@ +# An error in a multi-line command is reported at its first line. +catch { read_sdc include_error_line.sdc } msg +puts $msg + +# An error without a traceback does not reuse the read_sdc traceback. +catch { include include_error_line_missing.tcl } msg +puts [string first include_error_line_undefined_cmd $::errorInfo] diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index b3f3a6fb..d505dab5 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -157,6 +157,7 @@ record_public_tests { get_noargs get_scenes get_objrefs + include_error_line input_delay_ref_pin_rebuild liberty_arcs_one2one_1 liberty_arcs_one2one_2 From 8b4cc283bea33764ba96fb935e6e2732a8708779 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Wed, 30 Sep 2026 11:11:09 -0700 Subject: [PATCH 13/52] untabify Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- sdc/Sdc.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 6a5f5829..812e4561 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -5781,15 +5781,15 @@ Sdc::deletePinBefore(const Pin *pin) for (ExceptionPath *exception : itr->second) { ExceptionFrom *from = exception->from(); if (from) - from->deletePinBefore(pin, network_); + from->deletePinBefore(pin, network_); ExceptionTo *to = exception->to(); if (to) - to->deletePinBefore(pin, network_); + to->deletePinBefore(pin, network_); ExceptionPt *first_pt = exception->firstPt(); ExceptionThruSeq *thrus = exception->thrus(); if (thrus) { for (ExceptionThru *thru : *exception->thrus()) { - thru->deletePinBefore(pin, network_); + thru->deletePinBefore(pin, network_); if (thru == first_pt) recordExceptionEdges(exception, thru->edges(), first_thru_edge_exceptions_); From 7a034b56de14b485ddf8fd97b420a6f37a91c00d Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Thu, 1 Oct 2026 18:40:35 -0700 Subject: [PATCH 14/52] GraphDelayCalc::findDriverDelays !! Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 97a9c138..4d34245c 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -805,7 +805,7 @@ GraphDelayCalc::findDriverDelays(Vertex *drvr_vertex, { MultiDrvrNet *multi_drvr = findMultiDrvrNet(drvr_vertex); if (multi_drvr) { - if (!!multi_drvr->loadSlewsInited()) + if (!multi_drvr->loadSlewsInited()) initLoadSlews(drvr_vertex); multi_drvr->setLoadSlewsInited(true); } From 1d9a5f34eda25ae9b7f84745d2336f9519062182 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Mon, 5 Oct 2026 21:57:38 +0000 Subject: [PATCH 15/52] test: remove PortDirection::init() calls from gtests PortDirection::init/destroy were removed in f3cd9b1e (#521); the directions are now static objects that need no initialization. Drop the stale init() calls and the null-check guards around them. Signed-off-by: dsengupta0628 --- liberty/test/cpp/TestLibertyClasses.cc | 1 - liberty/test/cpp/TestLibertyStaBasicsB.cc | 1 - network/test/cpp/TestNetwork.cc | 40 ----------------------- network/test/cpp/TestNetworkB.cc | 6 ---- verilog/test/cpp/TestVerilog.cc | 21 ------------ 5 files changed, 69 deletions(-) diff --git a/liberty/test/cpp/TestLibertyClasses.cc b/liberty/test/cpp/TestLibertyClasses.cc index 27f5143c..0a72331d 100644 --- a/liberty/test/cpp/TestLibertyClasses.cc +++ b/liberty/test/cpp/TestLibertyClasses.cc @@ -2630,7 +2630,6 @@ TEST(LibertyLibraryTest, MakeScaledCell) { } TEST(LibertyLibraryTest, DefaultPinResistanceWithDirection) { - PortDirection::init(); LibertyLibrary lib("test_lib", "test.lib"); float res; bool exists; diff --git a/liberty/test/cpp/TestLibertyStaBasicsB.cc b/liberty/test/cpp/TestLibertyStaBasicsB.cc index 9834e42b..fb0f6400 100644 --- a/liberty/test/cpp/TestLibertyStaBasicsB.cc +++ b/liberty/test/cpp/TestLibertyStaBasicsB.cc @@ -1352,7 +1352,6 @@ TEST(R6_LibertyLibraryTest, DefaultBidirectPinResBoth) { } TEST(R6_LibertyLibraryTest, DefaultInoutPinRes) { - PortDirection::init(); LibertyLibrary lib("test_lib", "test.lib"); float res; bool exists; diff --git a/network/test/cpp/TestNetwork.cc b/network/test/cpp/TestNetwork.cc index 7ddb3cca..881b85b7 100644 --- a/network/test/cpp/TestNetwork.cc +++ b/network/test/cpp/TestNetwork.cc @@ -85,14 +85,6 @@ TEST_F(VerilogNamespaceTest, EscapedNameRoundTrip) { //////////////////////////////////////////////////////////////// class PortDirectionTest : public ::testing::Test { -protected: - void SetUp() override { - // PortDirection::init() should have been called by initSta or similar. - // If not already initialized, we need to call it. - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } - } }; TEST_F(PortDirectionTest, InputSingleton) { @@ -425,9 +417,6 @@ TEST(ConcretePortTest, ScalarPortProperties) { } TEST(ConcretePortTest, SetDirection) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } ConcreteLibrary lib("test_lib", "test.lib", false); ConcreteCell *cell = lib.makeCell("INV", true, ""); ConcretePort *port = cell->makePort("A"); @@ -755,9 +744,6 @@ TEST(ConcretePortTest, BusBitIndex) { } TEST(ConcretePortTest, SetDirectionOnBus) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } ConcreteLibrary lib("test_lib", "test.lib", false); lib.setBusBrkts('[', ']'); ConcreteCell *cell = lib.makeCell("REG", true, ""); @@ -797,9 +783,6 @@ TEST(ConcretePortTest, BusMemberIterator) { } TEST(ConcretePortTest, BundlePortSetDirection) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } ConcreteLibrary lib("test_lib", "test.lib", false); ConcreteCell *cell = lib.makeCell("MUX", true, ""); ConcretePort *a = cell->makePort("A"); @@ -851,9 +834,6 @@ TEST(ConcreteLibraryTest, FilenameAndId) { //////////////////////////////////////////////////////////////// TEST(PortDirectionExtraTest, AllDirections) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } EXPECT_NE(PortDirection::input(), nullptr); EXPECT_NE(PortDirection::output(), nullptr); EXPECT_NE(PortDirection::bidirect(), nullptr); @@ -866,9 +846,6 @@ TEST(PortDirectionExtraTest, AllDirections) { } TEST(PortDirectionExtraTest, DirectionProperties) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } EXPECT_TRUE(PortDirection::input()->isInput()); EXPECT_FALSE(PortDirection::input()->isOutput()); EXPECT_FALSE(PortDirection::input()->isBidirect()); @@ -888,9 +865,6 @@ TEST(PortDirectionExtraTest, DirectionProperties) { } TEST(PortDirectionExtraTest, DirectionNames) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } EXPECT_EQ(PortDirection::input()->name(), "input"); EXPECT_EQ(PortDirection::output()->name(), "output"); EXPECT_EQ(PortDirection::bidirect()->name(), "bidirect"); @@ -903,9 +877,6 @@ TEST(PortDirectionExtraTest, DirectionNames) { } TEST(PortDirectionExtraTest, FindAllByName) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } EXPECT_EQ(PortDirection::find("input"), PortDirection::input()); EXPECT_EQ(PortDirection::find("output"), PortDirection::output()); EXPECT_EQ(PortDirection::find("bidirect"), PortDirection::bidirect()); @@ -919,9 +890,6 @@ TEST(PortDirectionExtraTest, FindAllByName) { } TEST(PortDirectionExtraTest, DirectionIndex) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } // Each direction should have a unique index EXPECT_NE(PortDirection::input()->index(), PortDirection::output()->index()); EXPECT_NE(PortDirection::bidirect()->index(), PortDirection::tristate()->index()); @@ -932,9 +900,6 @@ TEST(PortDirectionExtraTest, DirectionIndex) { //////////////////////////////////////////////////////////////// TEST(NetworkCmpTest, PortDirectionCmp) { - if (PortDirection::input() == nullptr) { - PortDirection::init(); - } // PortDirection comparison is by index EXPECT_TRUE(PortDirection::input()->index() != PortDirection::output()->index()); } @@ -1254,7 +1219,6 @@ TEST(ConcreteNetworkTest, ConstLibertyCellFromCell) { } TEST(ConcreteNetworkTest, FindCellsMatchingViaNetwork) { - PortDirection::init(); ConcreteNetwork network; Library *lib = network.makeLibrary("match_lib", "match.lib"); network.makeCell(lib, "INV_X1", true, "match.lib"); @@ -1273,7 +1237,6 @@ TEST(ConcreteNetworkTest, FindCellsMatchingViaNetwork) { class ConcreteNetworkLinkedTest : public ::testing::Test { protected: void SetUp() override { - PortDirection::init(); // Build a simple network: top instance with 2 children lib_ = network_.makeLibrary("test_lib", "test.lib"); Cell *inv_cell = network_.makeCell(lib_, "INV", true, "test.lib"); @@ -1946,7 +1909,6 @@ TEST(ConcreteNetworkExtraTest, SetLinkFunc) { // Network: setCellNetworkView, cellNetworkView, deleteCellNetworkViews TEST(ConcreteNetworkExtraTest, CellNetworkView) { - PortDirection::init(); ConcreteNetwork network; Library *lib = network.makeLibrary("view_lib", "view.lib"); Cell *cell = network.makeCell(lib, "CELL1", true, "view.lib"); @@ -1996,7 +1958,6 @@ TEST(ConcreteNetworkExtraTest, NextObjectId) { // Network: deleteTopInstance TEST(ConcreteNetworkExtraTest, DeleteTopInstance) { - PortDirection::init(); ConcreteNetwork network; Library *lib = network.makeLibrary("del_top_lib", "del_top.lib"); Cell *cell = network.makeCell(lib, "TOP", false, "del_top.lib"); @@ -2719,7 +2680,6 @@ TEST(ConcretePortTest, BusPortDefaultConstructor) { ConcreteCell *cell = lib.makeCell("REG", true, ""); ConcretePort *bus = cell->makeBusPort("D", 1, 0); EXPECT_TRUE(bus->isBus()); - PortDirection::init(); bus->setDirection(PortDirection::input()); EXPECT_EQ(bus->direction(), PortDirection::input()); } diff --git a/network/test/cpp/TestNetworkB.cc b/network/test/cpp/TestNetworkB.cc index 93bde840..caffe289 100644 --- a/network/test/cpp/TestNetworkB.cc +++ b/network/test/cpp/TestNetworkB.cc @@ -20,7 +20,6 @@ namespace sta { class ConcreteNetworkLinkedTest : public ::testing::Test { protected: void SetUp() override { - PortDirection::init(); // Build a simple network: top instance with 2 children lib_ = network_.makeLibrary("test_lib", "test.lib"); Cell *inv_cell = network_.makeCell(lib_, "INV", true, "test.lib"); @@ -87,7 +86,6 @@ class ConcreteNetworkLinkedTest : public ::testing::Test { class NetworkAdapterTest : public ::testing::Test { protected: void SetUp() override { - PortDirection::init(); // Build a simple network lib_ = network_.makeLibrary("adapter_lib", "adapter.lib"); Cell *inv_cell = network_.makeCell(lib_, "BUF", true, "adapter.lib"); @@ -1249,7 +1247,6 @@ TEST(ConcretePortTest, BusPortDefaultCtor) { // BusPort: setDirection propagates to members TEST(ConcretePortTest, BusPortSetDirection) { - PortDirection::init(); ConcreteLibrary lib("test_lib", "test.lib", false); lib.setBusBrkts('[', ']'); ConcreteCell *cell = lib.makeCell("R6_BUSDIR", true, ""); @@ -1768,7 +1765,6 @@ TEST_F(ConcreteNetworkLinkedTest, MergeNets) { // BusPort: setDirection exercises BusPort::setDirection TEST(ConcretePortTest, BusPortSetDirectionInput) { - PortDirection::init(); ConcreteLibrary lib("test_lib", "test.lib", false); lib.setBusBrkts('[', ']'); ConcreteCell *cell = lib.makeCell("R7_BDIR", true, ""); @@ -2396,8 +2392,6 @@ TEST(ConcretePortR8Test, PortMemberIteratorOnScalar) { // BusPort::setDirection - uncovered TEST(ConcretePortR8Test, BusPortSetDirection) { - if (PortDirection::input() == nullptr) - PortDirection::init(); ConcreteLibrary lib("r8_lib3", "r8.lib", false); lib.setBusBrkts('[', ']'); ConcreteCell *cell = lib.makeCell("DIR_CELL", true, ""); diff --git a/verilog/test/cpp/TestVerilog.cc b/verilog/test/cpp/TestVerilog.cc index cd85f2c6..42701276 100644 --- a/verilog/test/cpp/TestVerilog.cc +++ b/verilog/test/cpp/TestVerilog.cc @@ -852,7 +852,6 @@ TEST_F(VerilogTest, ModuleInstModuleName) { // VerilogDcl: constructor with args seq TEST_F(VerilogTest, DclConstructSeq) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("wire1")); args->push_back(new VerilogDclArg("wire2")); @@ -865,7 +864,6 @@ TEST_F(VerilogTest, DclConstructSeq) { // VerilogDcl: portName TEST_F(VerilogTest, DclPortName) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("my_wire")); VerilogDcl dcl(PortDirection::output(), args, new VerilogAttrStmtSeq, 1); @@ -875,7 +873,6 @@ TEST_F(VerilogTest, DclPortName) { // VerilogDcl: appendArg TEST_F(VerilogTest, DclAppendArg) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("w1")); VerilogDcl dcl(PortDirection::input(), args, new VerilogAttrStmtSeq, 1); @@ -885,7 +882,6 @@ TEST_F(VerilogTest, DclAppendArg) { // VerilogDclBus: constructor with args seq TEST_F(VerilogTest, DclBusConstructSeq) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("bus_wire")); VerilogDclBus dcl(PortDirection::input(), 7, 0, args, @@ -899,7 +895,6 @@ TEST_F(VerilogTest, DclBusConstructSeq) { // VerilogDclBus: constructor with single arg TEST_F(VerilogTest, DclBusConstructSingle) { - PortDirection::init(); VerilogDclArg *arg = new VerilogDclArg("single_bus"); VerilogDclBus dcl(PortDirection::output(), 3, 0, arg, new VerilogAttrStmtSeq, 1); @@ -909,7 +904,6 @@ TEST_F(VerilogTest, DclBusConstructSingle) { // VerilogDclBus: ascending range TEST_F(VerilogTest, DclBusAscending) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("asc_bus")); VerilogDclBus dcl(PortDirection::input(), 0, 7, args, @@ -975,7 +969,6 @@ TEST_F(VerilogTest, PortRefPartAsc) { // VerilogDcl: single arg constructor TEST_F(VerilogTest, DclSingleArg) { - PortDirection::init(); VerilogDclArg *arg = new VerilogDclArg("single_wire"); VerilogDcl dcl(PortDirection::input(), arg, new VerilogAttrStmtSeq, 1); EXPECT_TRUE(dcl.isDeclaration()); @@ -1200,7 +1193,6 @@ TEST_F(VerilogTest, ModuleInstSetName) { // VerilogDcl: constructor with single arg TEST_F(VerilogTest, DclSingleArg2) { - PortDirection::init(); VerilogDclArg *arg = new VerilogDclArg("single_wire"); VerilogDcl dcl(PortDirection::input(), arg, new VerilogAttrStmtSeq, 5); EXPECT_TRUE(dcl.isDeclaration()); @@ -1211,7 +1203,6 @@ TEST_F(VerilogTest, DclSingleArg2) { // VerilogDcl: output direction TEST_F(VerilogTest, DclOutputDirection) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("out_wire")); VerilogDcl dcl(PortDirection::output(), args, new VerilogAttrStmtSeq, 1); @@ -1220,7 +1211,6 @@ TEST_F(VerilogTest, DclOutputDirection) { // VerilogDcl: size TEST_F(VerilogTest, DclSize) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("w1")); VerilogDcl dcl(PortDirection::input(), args, new VerilogAttrStmtSeq, 1); @@ -1229,7 +1219,6 @@ TEST_F(VerilogTest, DclSize) { // VerilogDclBus: size calculation TEST_F(VerilogTest, DclBusSize) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("bus_w")); VerilogDclBus dcl(PortDirection::input(), 31, 0, args, @@ -1240,7 +1229,6 @@ TEST_F(VerilogTest, DclBusSize) { // VerilogDclBus: ascending index TEST_F(VerilogTest, DclBusAscending2) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("bus_asc")); VerilogDclBus dcl(PortDirection::input(), 0, 7, args, @@ -1252,7 +1240,6 @@ TEST_F(VerilogTest, DclBusAscending2) { // VerilogDclBus: single-arg constructor TEST_F(VerilogTest, DclBusSingleArg) { - PortDirection::init(); VerilogDclArg *arg = new VerilogDclArg("single_bus"); VerilogDclBus dcl(PortDirection::output(), 3, 0, arg, new VerilogAttrStmtSeq, 1); @@ -1453,7 +1440,6 @@ TEST_F(VerilogTest, PortToStaEscapedBracket) { // VerilogDcl: appendArg TEST_F(VerilogTest, DclAppendMultiple) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("w1")); VerilogDcl dcl(PortDirection::input(), args, new VerilogAttrStmtSeq, 1); @@ -1613,7 +1599,6 @@ TEST_F(VerilogTest, ModuleInstMixedPins) { // VerilogDcl: various directions TEST_F(VerilogTest, DclBidirectional) { - PortDirection::init(); VerilogDclArg *arg = new VerilogDclArg("bidir_port"); VerilogDcl dcl(PortDirection::bidirect(), arg, new VerilogAttrStmtSeq, 1); EXPECT_TRUE(dcl.isDeclaration()); @@ -1622,7 +1607,6 @@ TEST_F(VerilogTest, DclBidirectional) { // VerilogDcl: appendArg to seq TEST_F(VerilogTest, DclAppendArgMultiple) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("w1")); VerilogDcl dcl(PortDirection::input(), args, new VerilogAttrStmtSeq, 1); @@ -1634,7 +1618,6 @@ TEST_F(VerilogTest, DclAppendArgMultiple) { // VerilogDclBus: large bus TEST_F(VerilogTest, DclBusLarge) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("wide_bus")); VerilogDclBus dcl(PortDirection::input(), 127, 0, args, @@ -1645,7 +1628,6 @@ TEST_F(VerilogTest, DclBusLarge) { // VerilogDclBus: descending range TEST_F(VerilogTest, DclBusDescending) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("desc_bus")); VerilogDclBus dcl(PortDirection::output(), 15, 8, args, @@ -1786,7 +1768,6 @@ TEST_F(VerilogTest, DclArgLongName) { // VerilogDcl: portName with bus arg TEST_F(VerilogTest, DclBusPortName) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("bus_port")); VerilogDclBus dcl(PortDirection::input(), 7, 0, args, @@ -1910,7 +1891,6 @@ TEST_F(VerilogTest, DclArgBasic) { // Test VerilogDcl portName // Covers: VerilogDcl::portName TEST_F(VerilogTest, DclPortName2) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("my_port")); VerilogDcl dcl(PortDirection::output(), args, new VerilogAttrStmtSeq, 1); @@ -1920,7 +1900,6 @@ TEST_F(VerilogTest, DclPortName2) { // Test VerilogDclBus with different ranges // Covers: VerilogDclBus constructor, portName with different bit ranges TEST_F(VerilogTest, DclBusDifferentRange) { - PortDirection::init(); VerilogDclArgSeq *args = new VerilogDclArgSeq; args->push_back(new VerilogDclArg("wide_bus")); VerilogDclBus dcl(PortDirection::bidirect(), 31, 0, args, From 0e543b310c2de400fdf9b25b09d473bcbe123de5 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Mon, 5 Oct 2026 21:57:38 +0000 Subject: [PATCH 16/52] test: update MultiDrvrNet gtests for loadSlewsInited API MultiDrvrNet::dcalcDrvr/setDcalcDrvr were removed in bdd66c5a ("dcalc multi-drvr do not use levels") and replaced by loadSlewsInited/setLoadSlewsInited. Retarget the accessor tests to the new API. Signed-off-by: dsengupta0628 --- dcalc/test/cpp/TestDcalc.cc | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/dcalc/test/cpp/TestDcalc.cc b/dcalc/test/cpp/TestDcalc.cc index bb04538c..7a057bfa 100644 --- a/dcalc/test/cpp/TestDcalc.cc +++ b/dcalc/test/cpp/TestDcalc.cc @@ -1136,21 +1136,18 @@ TEST_F(StaDcalcTest, GraphDelayCalcIncrementalTolerance) { gdc->setIncrementalDelayTolerance(tol); } -// Test MultiDrvrNet default construction and setDcalcDrvr +// Test MultiDrvrNet default construction TEST_F(StaDcalcTest, MultiDrvrNetConstruct) { MultiDrvrNet mdn; - EXPECT_EQ(mdn.dcalcDrvr(), nullptr); + EXPECT_FALSE(mdn.loadSlewsInited()); EXPECT_TRUE(mdn.drvrs().empty()); } -// Test MultiDrvrNet setDcalcDrvr -TEST_F(StaDcalcTest, MultiDrvrNetSetDcalcDrvr) { +// Test MultiDrvrNet setLoadSlewsInited +TEST_F(StaDcalcTest, MultiDrvrNetSetLoadSlewsInited) { MultiDrvrNet mdn; - // Use a dummy vertex pointer - int dummy = 42; - Vertex *v = reinterpret_cast(&dummy); - mdn.setDcalcDrvr(v); - EXPECT_EQ(mdn.dcalcDrvr(), v); + mdn.setLoadSlewsInited(true); + EXPECT_TRUE(mdn.loadSlewsInited()); } // Test dmp_ceff_two_pole inputPortDelay @@ -3366,13 +3363,12 @@ TEST_F(FindRootAdditionalTest, FourArgNegBracket) { // R9_ MultiDrvrNet set and reset TEST_F(StaDcalcTest, MultiDrvrNetSetReset) { MultiDrvrNet mdn; - int d1=1,d2=2; - mdn.setDcalcDrvr(reinterpret_cast(&d1)); - EXPECT_EQ(mdn.dcalcDrvr(), reinterpret_cast(&d1)); - mdn.setDcalcDrvr(reinterpret_cast(&d2)); - EXPECT_EQ(mdn.dcalcDrvr(), reinterpret_cast(&d2)); - mdn.setDcalcDrvr(nullptr); - EXPECT_EQ(mdn.dcalcDrvr(), nullptr); + mdn.setLoadSlewsInited(true); + EXPECT_TRUE(mdn.loadSlewsInited()); + mdn.setLoadSlewsInited(false); + EXPECT_FALSE(mdn.loadSlewsInited()); + mdn.setLoadSlewsInited(true); + EXPECT_TRUE(mdn.loadSlewsInited()); } // R9_ All calcs copyState twice From 2bdc03f8985dc46e1c3976c5baadc28db6c51db5 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Mon, 5 Oct 2026 21:57:38 +0000 Subject: [PATCH 17/52] test: update util goldens for findDelays level debug output Sta::findDelays() now passes Graph::vertex_level_max instead of levelize_->maxLevel() (e3326b03), so the delay_calc debug message reports level 16777215. Timing results are unchanged. Signed-off-by: dsengupta0628 --- util/test/util_parallel_misc.ok | 2 +- util/test/util_pattern_string.ok | 2 +- util/test/util_report_debug.ok | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/util/test/util_parallel_misc.ok b/util/test/util_parallel_misc.ok index aacf163b..1c66b743 100644 --- a/util/test/util_parallel_misc.ok +++ b/util/test/util_parallel_misc.ok @@ -139,7 +139,7 @@ Path Type: max 9.84 slack (MET) -delay_calc: find delays to level 90 +delay_calc: find delays to level 16777215 delay_calc: found 0 delays Startpoint: data_a[6] (input port clocked by clk) Endpoint: carry (output port clocked by clk) diff --git a/util/test/util_pattern_string.ok b/util/test/util_pattern_string.ok index 0887fada..084781cf 100644 --- a/util/test/util_pattern_string.ok +++ b/util/test/util_pattern_string.ok @@ -24,7 +24,7 @@ DFF_X? lib_cells: 2 * lib_cells: 134 --- set_debug with level > 0 --- delay_calc: delays invalid -delay_calc: find delays to level 50 +delay_calc: find delays to level 16777215 delay_calc: found 9 delays search: find arrivals pass 1 search: find arrivals to level 50 diff --git a/util/test/util_report_debug.ok b/util/test/util_report_debug.ok index fdd09ee6..77d37bae 100644 --- a/util/test/util_report_debug.ok +++ b/util/test/util_report_debug.ok @@ -35,7 +35,7 @@ Cell: BUF_X1 Arc sense: positive_unate Arc type: combinational A ^ -> Z ^ -delay_calc: find delays to level 50 +delay_calc: find delays to level 16777215 delay_calc: find delays reg1/Q (DFF_X1) delay_calc: find delays out1 (dcalc_test1) delay_calc: found 2 delays @@ -64,7 +64,7 @@ Driver waveform slew = 0.01 ............................................. A v -> Z v -delay_calc: find delays to level 50 +delay_calc: find delays to level 16777215 delay_calc: found 0 delays P = 1.00 V = 1.10 T = 25.00 ------- input_net_transition = 0.00 From f439bc8017b8de67623192bbc6cfc7adcde1cd91 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Mon, 5 Oct 2026 19:01:26 -0700 Subject: [PATCH 18/52] dcalc multi driver threads Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 4d34245c..ff594c8c 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -804,14 +804,15 @@ GraphDelayCalc::findDriverDelays(Vertex *drvr_vertex, LoadPinIndexMap &load_pin_index_map) { MultiDrvrNet *multi_drvr = findMultiDrvrNet(drvr_vertex); - if (multi_drvr) { - if (!multi_drvr->loadSlewsInited()) - initLoadSlews(drvr_vertex); - multi_drvr->setLoadSlewsInited(true); + if (multi_drvr == nullptr) { + initLoadSlews(drvr_vertex); + findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc, load_pin_index_map); } - else + else if (drvr_vertex == multi_drvr->drvrs()[0]) { initLoadSlews(drvr_vertex); - findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc, load_pin_index_map); + for (Vertex *drvr : multi_drvr->drvrs()) + findDriverDelays1(drvr, multi_drvr, arc_delay_calc, load_pin_index_map); + } arc_delay_calc->finishDrvrPin(); } @@ -884,7 +885,7 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) { Vertex *load_vertex = firstLoad(drvr_vertex); if (load_vertex) { - debugPrint(debug_, "delay_calc", 3, "multi-driver net"); + debugPrint(debug_, "dcalc_multi_drvr", 1, "multi-driver net"); MultiDrvrNet *multi_drvr = new MultiDrvrNet; VertexSeq &drvr_vertices = multi_drvr->drvrs(); VertexInEdgeIterator edge_iter(load_vertex, graph_); @@ -894,8 +895,9 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) Vertex *drvr = edge->from(graph_); const Pin *drvr_pin = drvr->pin(); if (isLeafDriver(drvr_pin, network_)) { - debugPrint(debug_, "delay_calc", 3, " {}", - network_->pathName(drvr_pin)); + debugPrint(debug_, "dcalc_multi_drvr", 1, " {} {}", + sdc_network_->pathName(drvr_pin), + network_->direction(drvr_pin)->name()); multi_drvr_net_map_[drvr] = multi_drvr; drvr_vertices.push_back(drvr); } @@ -1805,7 +1807,7 @@ MultiDrvrNet::findCaps(const StaState *sta) float pin_cap, wire_cap, fanout; bool has_net_load; // Find pin and external pin/wire capacitance. - sdc->connectedCap(drvr_pin, drvr_rf, scene, min_max, + sdc->connectedCap(drvr_pin, drvr_rf, scene, min_max, pin_cap, wire_cap, fanout, has_net_load); net_caps.init(pin_cap, wire_cap, fanout, has_net_load); } From 9ee64aeafcd9b919c5d4983271fb390f4b109a9f Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:23:08 -0700 Subject: [PATCH 19/52] back out part of bdd66c5a Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 88 +++++++++++++++++++---------------- include/sta/GraphDelayCalc.hh | 12 +++-- 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index ff594c8c..e8962923 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -254,7 +254,7 @@ GraphDelayCalc::delayInvalid(Vertex *vertex) // Invalidate driver that triggers dcalc for multi-driver nets. MultiDrvrNet *multi_drvr = multiDrvrNet(vertex); if (multi_drvr) - multi_drvr->setLoadSlewsInited(false); + invalid_delays_.insert(multi_drvr->dcalcDrvr()); } } @@ -808,7 +808,7 @@ GraphDelayCalc::findDriverDelays(Vertex *drvr_vertex, initLoadSlews(drvr_vertex); findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc, load_pin_index_map); } - else if (drvr_vertex == multi_drvr->drvrs()[0]) { + else if (drvr_vertex == multi_drvr->dcalcDrvr()) { initLoadSlews(drvr_vertex); for (Vertex *drvr : multi_drvr->drvrs()) findDriverDelays1(drvr, multi_drvr, arc_delay_calc, load_pin_index_map); @@ -853,14 +853,25 @@ GraphDelayCalc::hasMultiDrvrs(Vertex *drvr_vertex) return false; } +static bool +isLeafBidirectLoad(const Pin *pin, + const Network *network) +{ + PortDirection *dir = network->direction(pin); + const Instance *inst = network->instance(pin); + return network->isLeaf(inst) && dir->isBidirect(); +} + Vertex * GraphDelayCalc::firstLoad(Vertex *drvr_vertex) { VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); while (edge_iter.hasNext()) { Edge *wire_edge = edge_iter.next(); - if (wire_edge->isWire()) - return wire_edge->to(graph_); + Vertex *to = wire_edge->to(graph_); + if (wire_edge->isWire() + && !isLeafBidirectLoad(to->pin(), network_)) + return to; } return nullptr; } @@ -888,6 +899,8 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) debugPrint(debug_, "dcalc_multi_drvr", 1, "multi-driver net"); MultiDrvrNet *multi_drvr = new MultiDrvrNet; VertexSeq &drvr_vertices = multi_drvr->drvrs(); + Level max_drvr_level = 0; + Vertex *max_drvr = nullptr; VertexInEdgeIterator edge_iter(load_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); @@ -900,9 +913,16 @@ GraphDelayCalc::makeMultiDrvrNet(Vertex *drvr_vertex) network_->direction(drvr_pin)->name()); multi_drvr_net_map_[drvr] = multi_drvr; drvr_vertices.push_back(drvr); + Level drvr_level = drvr->level(); + if (max_drvr == nullptr + || drvr_level > max_drvr_level) { + max_drvr = drvr; + max_drvr_level = drvr_level; + } } } } + multi_drvr->setDcalcDrvr(max_drvr); multi_drvr->findCaps(this); return multi_drvr; } @@ -952,7 +972,7 @@ GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, } for (const RiseFall *rf : RiseFall::range()) { if (!delay_exists[rf->index()]) - zeroSlewAndWireDelays(drvr_vertex, rf); + zeroDrvrSlew(drvr_vertex, rf); } if (delay_changed && observer_) observer_->delayChangedTo(drvr_vertex); @@ -1504,27 +1524,15 @@ GraphDelayCalc::initSlew(Vertex *vertex) } void -GraphDelayCalc::zeroSlewAndWireDelays(Vertex *drvr_vertex, - const RiseFall *rf) +GraphDelayCalc::zeroDrvrSlew(Vertex *drvr_vertex, + const RiseFall *rf) { for (Scene *scene : scenes_) { for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - // Init drvr slew. + // Zero drvr slew. if (!drvr_vertex->slewAnnotated(rf, min_max)) - graph_->setSlew(drvr_vertex, rf, ap_index, min_max->initValue()); - - // Zero wire delays and slews. - 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_); - annotateLoadDelaySlew(drvr_vertex, wire_edge, delay_zero, - load_vertex, delay_zero, - true, rf, min_max, ap_index); - } - } + graph_->setSlew(drvr_vertex, rf, ap_index, delay_zero); } } } @@ -1793,38 +1801,38 @@ MultiDrvrNet::netCaps(const RiseFall *drvr_rf, void MultiDrvrNet::findCaps(const StaState *sta) { - int count = RiseFall::index_count * sta->dcalcAnalysisPtCount(); + size_t count = RiseFall::index_count * sta->dcalcAnalysisPtCount(); net_caps_.resize(count); - const Pin *drvr_pin = drvrs_[0]->pin(); + const Pin *drvr_pin = dcalc_drvr_->pin(); for (Scene *scene : sta->scenes()) { const Sdc *sdc = scene->sdc(); for (const MinMax *min_max : MinMax::range()) { DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); - for (const RiseFall *drvr_rf : RiseFall::range()) { - int drvr_rf_index = drvr_rf->index(); - int index = ap_index * RiseFall::index_count + drvr_rf_index; - NetCaps &net_caps = net_caps_[index]; - float pin_cap, wire_cap, fanout; - bool has_net_load; - // Find pin and external pin/wire capacitance. - sdc->connectedCap(drvr_pin, drvr_rf, scene, min_max, - pin_cap, wire_cap, fanout, has_net_load); - net_caps.init(pin_cap, wire_cap, fanout, has_net_load); + for (const RiseFall *drvr_rf : RiseFall::range()) { + size_t drvr_rf_index = drvr_rf->index(); + size_t index = ap_index * RiseFall::index_count + drvr_rf_index; + NetCaps &net_caps = net_caps_[index]; + float pin_cap, wire_cap, fanout; + bool has_net_load; + // Find pin and external pin/wire capacitance. + sdc->connectedCap(drvr_pin, drvr_rf, scene, min_max, + pin_cap, wire_cap, fanout, has_net_load); + net_caps.init(pin_cap, wire_cap, fanout, has_net_load); + } } } - } } -bool -MultiDrvrNet::parallelGates(const Network *network) const +void +MultiDrvrNet::setDcalcDrvr(Vertex *drvr) { - return network->direction(drvrs_[0]->pin())->isOutput(); + dcalc_drvr_ = drvr; } -void -MultiDrvrNet::setLoadSlewsInited(bool inited) +bool +MultiDrvrNet::parallelGates(const Network *network) const { - load_slews_inited_ = inited; + return network->direction(drvrs_[0]->pin())->isOutput(); } } // namespace sta diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 94302310..778765aa 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -230,8 +230,8 @@ protected: const TimingArc *&arc); void initWireDelays(Vertex *drvr_vertex); void initRootSlews(Vertex *vertex); - void zeroSlewAndWireDelays(Vertex *drvr_vertex, - const RiseFall *rf); + void zeroDrvrSlew(Vertex *drvr_vertex, + const RiseFall *rf); void findVertexDelay(Vertex *vertex, ArcDelayCalc *arc_delay_calc); DrvrLoadSlews loadSlews(LoadPinIndexMap &load_pin_index_map); @@ -345,6 +345,8 @@ class MultiDrvrNet public: VertexSeq &drvrs() { return drvrs_; } const VertexSeq &drvrs() const { return drvrs_; } + Vertex *dcalcDrvr() const { return dcalc_drvr_; } + void setDcalcDrvr(Vertex *drvr); bool parallelGates(const Network *network) const; void netCaps(const RiseFall *rf, const Scene *scene, @@ -355,14 +357,14 @@ public: float &fanout, bool &has_net_load) const; void findCaps(const StaState *sta); - bool loadSlewsInited() const { return load_slews_inited_; } - void setLoadSlewsInited(bool inited); private: VertexSeq drvrs_; + // Driver that triggers delay calculation for all the drivers on the net. + Vertex *dcalc_drvr_{nullptr}; // [drvr_rf->index][dcalc_ap->index] std::vector net_caps_; - bool load_slews_inited_{false}; + }; } // namespace sta From a1fa6347b8b666ba2da1785140b414f2d3d259ef Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Tue, 6 Oct 2026 18:29:44 -0700 Subject: [PATCH 20/52] Levelize::findBackEdges typo Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/Levelize.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/Levelize.cc b/search/Levelize.cc index 926a1240..6a39682b 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -242,7 +242,7 @@ Levelize::findBackEdges() EdgeSeq path; FindBackEdgesStack stack; VertexSeq roots = sortedRootsWithFanout(); - for (Vertex *vertex : roots_) { + for (Vertex *vertex : roots) { vertex->setVisited(true); vertex->setOnPath(true); stack.emplace(vertex, new VertexOutEdgeIterator(vertex, graph_)); From a2abda6ce0140ac55c80102f2d9ee4a8304fdf7f Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Mon, 5 Oct 2026 13:12:47 -0700 Subject: [PATCH 21/52] dcalc multi driver nets lock Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index e8962923..7b590f32 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -1103,8 +1103,14 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, load_cap, parasitic, load_pin_index_map, scene, min_max); - delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, - load_pin_index_map, scene, min_max); + if (multi_drvr) { + LockGuard lock(multi_drvr_lock_); + delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, + load_pin_index_map, scene, min_max); + } + else + delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, + load_pin_index_map, scene, min_max); } arc_delay_calc->finishDrvrPin(); } From a54fb95303063a720fbdb49c1d0da6f857118b1f Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sat, 3 Oct 2026 09:20:25 -0700 Subject: [PATCH 22/52] parasitics pre-build, rm lock Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- include/sta/Parasitics.hh | 3 +++ parasitics/ConcreteParasitics.cc | 13 +++++++++---- parasitics/ConcreteParasitics.hh | 2 ++ parasitics/Parasitics.cc | 22 ++++++++++++++++++++++ parasitics/SpefReader.cc | 2 ++ 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index c6706f90..1722efff 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -59,6 +59,9 @@ public: // Clear all state. virtual void clear() = 0; + virtual void ensureParasitics(); + virtual void ensureParasitics(const Pin *drvr_pin) = 0; + // Delete all parasitics. virtual void deleteParasitics() = 0; // Delete all parasitics on net at analysis point. diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 6ab2bcbb..80b0662a 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -907,12 +907,20 @@ minMaxRiseFallIndex(const MinMax *min_max, return min_max->index() * RiseFall::index_count + rf->index(); } +void +ConcreteParasitics::ensureParasitics(const Pin *drvr_pin) +{ + const Net *net = findParasiticNet(drvr_pin); + if (net) + parasitic_network_map_.try_emplace(net, net, false, network_); + drvr_parasitic_map_[drvr_pin]; +} + Parasitic * ConcreteParasitics::findPiElmore(const Pin *drvr_pin, const RiseFall *rf, const MinMax *min_max) const { - LockGuard lock(lock_); auto itr = drvr_parasitic_map_.find(drvr_pin); if (itr != drvr_parasitic_map_.end()) { const MinMaxRiseFallParasitics ¶sitics = itr->second; @@ -1138,7 +1146,6 @@ ConcreteParasitics::isParasiticNetwork(const Parasitic *parasitic) const Parasitic * ConcreteParasitics::findParasiticNetwork(const Net *net) { - LockGuard lock(lock_); auto itr = parasitic_network_map_.find(net); if (itr != parasitic_network_map_.end()) return &itr->second; @@ -1150,7 +1157,6 @@ Parasitic * ConcreteParasitics::findParasiticNetwork(const Pin *pin) { if (!parasitic_network_map_.empty()) { - LockGuard lock(lock_); if (!parasitic_network_map_.empty()) { // Only call findParasiticNet if parasitics exist. const Net *net = findParasiticNet(pin); @@ -1169,7 +1175,6 @@ Parasitic * ConcreteParasitics::makeParasiticNetwork(const Net *net, bool includes_pin_caps) { - LockGuard lock(lock_); auto itr = parasitic_network_map_.find(net); if (itr != parasitic_network_map_.end()) { parasitic_network_map_.erase(itr); diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 936609c1..992d0223 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -69,6 +69,8 @@ public: float capacitance(const Parasitic *parasitic) const override; + void ensureParasitics(const Pin *drvr_pin) override; + bool isPiElmore(const Parasitic *parasitic) const override; Parasitic *findPiElmore(const Pin *drvr_pin, const RiseFall *rf, diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index 086ad9b4..a31eb5f7 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -75,6 +75,28 @@ Parasitics::report(const Parasitic *parasitic) const } } +void +Parasitics::ensureParasitics() +{ + auto make_parasitics = [this] (const Instance *inst) { + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + if (network_->isDriver(pin)) + ensureParasitics(pin); + } + delete pin_iter; + }; + + LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator(); + while (leaf_iter->hasNext()) { + const Instance *inst = leaf_iter->next(); + make_parasitics(inst); + } + delete leaf_iter; + make_parasitics(network_->topInstance()); +} + const Net * Parasitics::findParasiticNet(const Pin *pin) const { diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 05141673..6f270b00 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -92,6 +92,8 @@ SpefReader::SpefReader(std::string_view filename, bool SpefReader::read() { + parasitics_->ensureParasitics(); + bool success; gzstream::igzstream stream(std::string(filename_).c_str()); if (stream.is_open()) { From 949da29975331559c35ecf6d915fbc9ef0c34fdf Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sat, 3 Oct 2026 11:07:35 -0700 Subject: [PATCH 23/52] spef lock passes fast Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/ArnoldiReduce.cc | 22 +++++++++++++--------- parasitics/ConcreteParasitics.cc | 16 ++++++++++++++-- parasitics/ConcreteParasiticsPvt.hh | 1 + search/Sta.cc | 4 ++++ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/dcalc/ArnoldiReduce.cc b/dcalc/ArnoldiReduce.cc index c9940ae4..339aba35 100644 --- a/dcalc/ArnoldiReduce.cc +++ b/dcalc/ArnoldiReduce.cc @@ -322,16 +322,20 @@ ArnoldiReduce::findPt(ParasiticNode *node) rcmodel * ArnoldiReduce::makeRcmodelDrv() { - ParasiticNode *drv_node = - parasitics_->findParasiticNode(parasitic_network_, drvr_pin_); - ts_point *pdrv = findPt(drv_node); - makeRcmodelDfs(pdrv); - getRC(); - if (ctot_ < 1e-22) // 1e-10ps + ParasiticNode *drv_node = parasitics_->findParasiticNode(parasitic_network_, + drvr_pin_); + if (drv_node) { + ts_point *pdrv = findPt(drv_node); + makeRcmodelDfs(pdrv); + getRC(); + if (ctot_ < 1e-22) // 1e-10ps + return nullptr; + setTerms(pdrv); + makeRcmodelFromTs(); + return makeRcmodelFromW(); + } + else return nullptr; - setTerms(pdrv); - makeRcmodelFromTs(); - return makeRcmodelFromW(); } #define ts_orient(pp, ee) \ diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 80b0662a..18b5ccaa 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -503,6 +503,13 @@ ConcreteParasiticNetwork::~ConcreteParasiticNetwork() deleteNodes(); } +bool +ConcreteParasiticNetwork::empty() const +{ + return sub_nodes_.empty() + && pin_nodes_.empty(); +} + void ConcreteParasiticNetwork::deleteNodes() { @@ -1163,8 +1170,13 @@ ConcreteParasitics::findParasiticNetwork(const Pin *pin) if (!parasitic_network_map_.empty()) { auto itr = parasitic_network_map_.find(net); - if (itr != parasitic_network_map_.end()) - return &itr->second; + if (itr != parasitic_network_map_.end()) { + ConcreteParasiticNetwork ¶sitic = itr->second; + if (parasitic.empty()) + return nullptr; + else + return ¶sitic; + } } } } diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index 8b36ff4a..7d68c95f 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -221,6 +221,7 @@ public: bool isParasiticNetwork() const override { return true; } const Net *net() const { return net_; } bool includesPinCaps() const { return includes_pin_caps_; } + bool empty() const; ConcreteParasiticNode *findParasiticNode(const Net *net, uint32_t id, const Network *network) const; diff --git a/search/Sta.cc b/search/Sta.cc index e70bc872..70960878 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4581,6 +4581,8 @@ Sta::makeInstanceAfter(const Instance *inst) search_->endpointInvalid(vertex); if (bidir_drvr_vertex) search_->endpointInvalid(bidir_drvr_vertex); + for (auto &[name, parasitics] : parasitics_name_map_) + parasitics->ensureParasitics(pin); } } graph_->makeInstanceEdges(inst); @@ -4595,6 +4597,8 @@ Sta::makePortPinAfter(Pin *pin) if (graph_) { Vertex *vertex, *bidir_drvr_vertex; graph_->makePinVertices(pin, vertex, bidir_drvr_vertex); + for (auto &[name, parasitics] : parasitics_name_map_) + parasitics->ensureParasitics(pin); } } From 47f91f2883bf4e4f153225f1645474d56e39ff27 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:22:01 -0700 Subject: [PATCH 24/52] spef lock passes fast w/threads Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- include/sta/Parasitics.hh | 2 ++ parasitics/ConcreteParasitics.cc | 52 ++++++++++++++++------------- parasitics/ConcreteParasitics.hh | 1 - parasitics/ConcreteParasiticsPvt.hh | 10 +++--- search/Sta.cc | 2 ++ 5 files changed, 37 insertions(+), 30 deletions(-) diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 1722efff..7b9b0a79 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -59,6 +59,8 @@ public: // Clear all state. virtual void clear() = 0; + // Ensure there is an empty parasitic for every driver in the network. + // This allows parasitic lookup without using thread locks. virtual void ensureParasitics(); virtual void ensureParasitics(const Pin *drvr_pin) = 0; diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 18b5ccaa..a18ff468 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -477,13 +477,9 @@ ConcreteParasiticCapacitor::ConcreteParasiticCapacitor(uint32_t id, //////////////////////////////////////////////////////////////// -ConcreteParasiticNetwork::ConcreteParasiticNetwork(const Net *net, - bool includes_pin_caps, - const Network *network) : - net_(net), +ConcreteParasiticNetwork::ConcreteParasiticNetwork(const Network *network) : sub_nodes_(network), - pin_nodes_(network), - includes_pin_caps_(includes_pin_caps) + pin_nodes_(network) { } @@ -503,6 +499,17 @@ ConcreteParasiticNetwork::~ConcreteParasiticNetwork() deleteNodes(); } +void +ConcreteParasiticNetwork::init(const Net *net, + bool includes_pin_caps) +{ + net_ = net; + includes_pin_caps_ = includes_pin_caps; + deleteDevices(); + deleteNodes(); + +} + bool ConcreteParasiticNetwork::empty() const { @@ -517,6 +524,8 @@ ConcreteParasiticNetwork::deleteNodes() delete node; for (const auto& [pin, node] : pin_nodes_) delete node; + sub_nodes_.clear(); + pin_nodes_.clear(); } void @@ -532,6 +541,8 @@ ConcreteParasiticNetwork::deleteDevices() reinterpret_cast(capacitor); delete ccapacitor; } + resistors_.clear(); + capacitors_.clear(); } void @@ -798,10 +809,13 @@ ConcreteParasitics::deleteParasitics(const Pin *drvr_pin) { auto itr = drvr_parasitic_map_.find(drvr_pin); if (itr != drvr_parasitic_map_.end()) { - const MinMaxRiseFallParasitics ¶sitics = itr->second; - for (size_t i = 0; i < min_max_rise_fall_count; i++) + MinMaxRiseFallParasitics ¶sitics = itr->second; + for (size_t i = 0; i < min_max_rise_fall_count; i++) { delete parasitics[i]; - drvr_parasitic_map_.erase(itr); + parasitics[i] = nullptr; + } + // Do NOT erase the drvr_parasitic_map_ entry because it is a placeholder + // so the entries are stable. } } @@ -919,7 +933,7 @@ ConcreteParasitics::ensureParasitics(const Pin *drvr_pin) { const Net *net = findParasiticNet(drvr_pin); if (net) - parasitic_network_map_.try_emplace(net, net, false, network_); + parasitic_network_map_.try_emplace(net, network_); drvr_parasitic_map_[drvr_pin]; } @@ -946,7 +960,6 @@ ConcreteParasitics::makePiElmore(const Pin *drvr_pin, float rpi, float c1) { - LockGuard lock(lock_); auto itr = drvr_parasitic_map_.find(drvr_pin); ConcretePiElmore *pi_elmore = nullptr; size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf); @@ -1044,7 +1057,6 @@ ConcreteParasitics::findPiPoleResidue(const Pin *drvr_pin, const RiseFall *rf, const MinMax *min_max) const { - LockGuard lock(lock_); auto itr = drvr_parasitic_map_.find(drvr_pin); if (itr != drvr_parasitic_map_.end()) { const MinMaxRiseFallParasitics ¶sitics = itr->second; @@ -1064,7 +1076,6 @@ ConcreteParasitics::makePiPoleResidue(const Pin *drvr_pin, float rpi, float c1) { - LockGuard lock(lock_); auto itr = drvr_parasitic_map_.find(drvr_pin); ConcretePiPoleResidue *pi_pole_residue = nullptr; size_t mm_rf_index = minMaxRiseFallIndex(min_max, rf); @@ -1187,22 +1198,15 @@ Parasitic * ConcreteParasitics::makeParasiticNetwork(const Net *net, bool includes_pin_caps) { - auto itr = parasitic_network_map_.find(net); - if (itr != parasitic_network_map_.end()) { - parasitic_network_map_.erase(itr); - for (const Pin *drvr_pin : *network_->drivers(net)) - deleteParasitics(drvr_pin); - } - parasitic_network_map_.emplace(net, ConcreteParasiticNetwork(net, includes_pin_caps, - network_)); - return ¶sitic_network_map_.find(net)->second; + ConcreteParasiticNetwork ¶sitic = parasitic_network_map_.find(net)->second; + parasitic.init(net, includes_pin_caps); + return ¶sitic; } void ConcreteParasitics::deleteParasiticNetwork(const Net *net) { - LockGuard lock(lock_); - parasitic_network_map_.erase(net); + parasitic_network_map_.find(net)->second.init(net, false); } const Net * diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 992d0223..25c08ddb 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -198,7 +198,6 @@ protected: // and transition. ConcreteParasiticMap drvr_parasitic_map_; ConcreteParasiticNetworkMap parasitic_network_map_; - mutable std::mutex lock_; friend class ConcretePiElmore; friend class ConcreteParasiticNode; diff --git a/parasitics/ConcreteParasiticsPvt.hh b/parasitics/ConcreteParasiticsPvt.hh index 7d68c95f..99784d24 100644 --- a/parasitics/ConcreteParasiticsPvt.hh +++ b/parasitics/ConcreteParasiticsPvt.hh @@ -213,12 +213,12 @@ class ConcreteParasiticNetwork : public ParasiticNetwork, public ConcreteParasitic { public: - ConcreteParasiticNetwork(const Net *net, - bool includes_pin_caps, - const Network *network); + ConcreteParasiticNetwork(const Network *network); ConcreteParasiticNetwork(ConcreteParasiticNetwork &¶sitic) noexcept; ~ConcreteParasiticNetwork() override; bool isParasiticNetwork() const override { return true; } + void init(const Net *net, + bool includes_pin_caps); const Net *net() const { return net_; } bool includesPinCaps() const { return includes_pin_caps_; } bool empty() const; @@ -255,13 +255,13 @@ private: void deleteNodes(); void deleteDevices(); - const Net *net_; + const Net *net_{nullptr}; ConcreteParasiticSubNodeMap sub_nodes_; ConcreteParasiticPinNodeMap pin_nodes_; ParasiticResistorSeq resistors_; ParasiticCapacitorSeq capacitors_; unsigned max_node_id_:31{0}; - bool includes_pin_caps_:1; + bool includes_pin_caps_:1{false}; }; class ConcreteParasiticNode : public ParasiticNode diff --git a/search/Sta.cc b/search/Sta.cc index 70960878..a0427e89 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -1064,6 +1064,8 @@ Sta::setWireload(Wireload *wireload, Sdc *sdc) { sdc->setWireload(wireload, min_max); + for (auto &[name, parasitics] : parasitics_name_map_) + parasitics->ensureParasitics(); delaysInvalid(); } From 5d1e863c7a4c63c91b6da41a9ee60996ca49f4bb Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sun, 4 Oct 2026 09:45:28 -0700 Subject: [PATCH 25/52] ConcreteParasitics::deleteNetBefore Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- include/sta/Parasitics.hh | 1 + parasitics/ConcreteParasitics.cc | 7 +++++++ parasitics/ConcreteParasitics.hh | 1 + search/Sta.cc | 2 ++ 4 files changed, 11 insertions(+) diff --git a/include/sta/Parasitics.hh b/include/sta/Parasitics.hh index 7b9b0a79..f594fde3 100644 --- a/include/sta/Parasitics.hh +++ b/include/sta/Parasitics.hh @@ -268,6 +268,7 @@ public: // Network edit before/after methods. virtual void disconnectPinBefore(const Pin *pin) = 0; virtual void deletePinBefore(const Pin *pin) = 0; + virtual void deleteNetBefore(const Net *net) = 0; virtual void loadPinCapacitanceChanged(const Pin *pin) = 0; float couplingCapFactor() const { return coupling_cap_factor_; } void setCouplingCapFactor(float factor); diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index a18ff468..0a37beb6 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -872,6 +872,13 @@ ConcreteParasitics::deletePinBefore(const Pin *pin) { // Actions are the same. disconnectPinBefore(pin); + drvr_parasitic_map_.erase(pin); +} + +void +ConcreteParasitics::deleteNetBefore(const Net *net) +{ + parasitic_network_map_.erase(net); } void diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index 25c08ddb..e76f2fae 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -180,6 +180,7 @@ public: void disconnectPinBefore(const Pin *pin) override; void deletePinBefore(const Pin *pin) override; + void deleteNetBefore(const Net *net) override; void loadPinCapacitanceChanged(const Pin *pin) override; void deleteReducedParasitics(const Net *net) override; diff --git a/search/Sta.cc b/search/Sta.cc index a0427e89..112c92e1 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -4958,6 +4958,8 @@ Sta::deleteNetBefore(const Net *net) } for (Mode *mode : modes_) mode->sdc()->deleteNetBefore(net); + for (auto [name, parasitics] : parasitics_name_map_) + parasitics->deleteNetBefore(net); clk_skews_->clear(); power_->powerInvalid(); } From b7d866ff69e618c025510423ffb65a202796b4f9 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sun, 4 Oct 2026 15:35:53 -0700 Subject: [PATCH 26/52] ConcreteParasitics::ensureParasitics use try_emplace Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- parasitics/ConcreteParasitics.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 0a37beb6..011eb272 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -941,7 +941,7 @@ ConcreteParasitics::ensureParasitics(const Pin *drvr_pin) const Net *net = findParasiticNet(drvr_pin); if (net) parasitic_network_map_.try_emplace(net, network_); - drvr_parasitic_map_[drvr_pin]; + drvr_parasitic_map_.try_emplace(drvr_pin); } Parasitic * From b1cb4aba085bfd7239445ecaf964add13f068aa5 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Wed, 7 Oct 2026 02:44:48 +0000 Subject: [PATCH 27/52] test: restore MultiDrvrNet dcalcDrvr gtests Upstream 9ee64aea ("back out part of bdd66c5a") restored MultiDrvrNet::dcalcDrvr/setDcalcDrvr and removed loadSlewsInited. Revert TestDcalc.cc to its state before 0e543b31. Signed-off-by: dsengupta0628 --- dcalc/test/cpp/TestDcalc.cc | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/dcalc/test/cpp/TestDcalc.cc b/dcalc/test/cpp/TestDcalc.cc index 7a057bfa..bb04538c 100644 --- a/dcalc/test/cpp/TestDcalc.cc +++ b/dcalc/test/cpp/TestDcalc.cc @@ -1136,18 +1136,21 @@ TEST_F(StaDcalcTest, GraphDelayCalcIncrementalTolerance) { gdc->setIncrementalDelayTolerance(tol); } -// Test MultiDrvrNet default construction +// Test MultiDrvrNet default construction and setDcalcDrvr TEST_F(StaDcalcTest, MultiDrvrNetConstruct) { MultiDrvrNet mdn; - EXPECT_FALSE(mdn.loadSlewsInited()); + EXPECT_EQ(mdn.dcalcDrvr(), nullptr); EXPECT_TRUE(mdn.drvrs().empty()); } -// Test MultiDrvrNet setLoadSlewsInited -TEST_F(StaDcalcTest, MultiDrvrNetSetLoadSlewsInited) { +// Test MultiDrvrNet setDcalcDrvr +TEST_F(StaDcalcTest, MultiDrvrNetSetDcalcDrvr) { MultiDrvrNet mdn; - mdn.setLoadSlewsInited(true); - EXPECT_TRUE(mdn.loadSlewsInited()); + // Use a dummy vertex pointer + int dummy = 42; + Vertex *v = reinterpret_cast(&dummy); + mdn.setDcalcDrvr(v); + EXPECT_EQ(mdn.dcalcDrvr(), v); } // Test dmp_ceff_two_pole inputPortDelay @@ -3363,12 +3366,13 @@ TEST_F(FindRootAdditionalTest, FourArgNegBracket) { // R9_ MultiDrvrNet set and reset TEST_F(StaDcalcTest, MultiDrvrNetSetReset) { MultiDrvrNet mdn; - mdn.setLoadSlewsInited(true); - EXPECT_TRUE(mdn.loadSlewsInited()); - mdn.setLoadSlewsInited(false); - EXPECT_FALSE(mdn.loadSlewsInited()); - mdn.setLoadSlewsInited(true); - EXPECT_TRUE(mdn.loadSlewsInited()); + int d1=1,d2=2; + mdn.setDcalcDrvr(reinterpret_cast(&d1)); + EXPECT_EQ(mdn.dcalcDrvr(), reinterpret_cast(&d1)); + mdn.setDcalcDrvr(reinterpret_cast(&d2)); + EXPECT_EQ(mdn.dcalcDrvr(), reinterpret_cast(&d2)); + mdn.setDcalcDrvr(nullptr); + EXPECT_EQ(mdn.dcalcDrvr(), nullptr); } // R9_ All calcs copyState twice From ed7157bd7c33b084fa5504656a6eba5defd2306d Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Wed, 7 Oct 2026 02:44:48 +0000 Subject: [PATCH 28/52] test: update ConcreteParasiticNetwork ctor in parasitics gtests Upstream 47f91f28 changed the ConcreteParasiticNetwork constructor to take only the Network; net and includes_pin_caps are now set by init(). Construct with (network) and call init(nullptr, true) where the tests need includesPinCaps() to be true. Signed-off-by: dsengupta0628 --- parasitics/test/cpp/TestParasitics.cc | 63 ++++++++++++++------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/parasitics/test/cpp/TestParasitics.cc b/parasitics/test/cpp/TestParasitics.cc index 6d59a12d..22dc3a12 100644 --- a/parasitics/test/cpp/TestParasitics.cc +++ b/parasitics/test/cpp/TestParasitics.cc @@ -1328,7 +1328,7 @@ TEST_F(StaParasiticsTest, ParasiticNetworkEmptyNodes) { // ConcreteParasiticNetwork requires a Network* for its constructor // so we need to pass sta_->network() const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); ParasiticNodeSeq nodes = pnet.nodes(); EXPECT_TRUE(nodes.empty()); } @@ -1336,7 +1336,7 @@ TEST_F(StaParasiticsTest, ParasiticNetworkEmptyNodes) { // Test ConcreteParasiticNetwork resistors/capacitors empty TEST_F(StaParasiticsTest, ParasiticNetworkEmptyDevices) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); EXPECT_TRUE(pnet.resistors().empty()); EXPECT_TRUE(pnet.capacitors().empty()); } @@ -1344,38 +1344,39 @@ TEST_F(StaParasiticsTest, ParasiticNetworkEmptyDevices) { // Test ConcreteParasiticNetwork capacitance with no devices TEST_F(StaParasiticsTest, ParasiticNetworkZeroCapacitance) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); EXPECT_FLOAT_EQ(pnet.capacitance(), 0.0f); } // Test ConcreteParasiticNetwork isParasiticNetwork TEST_F(StaParasiticsTest, ParasiticNetworkIsNetwork) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); EXPECT_TRUE(pnet.isParasiticNetwork()); } // Test ConcreteParasiticNetwork net() TEST_F(StaParasiticsTest, ParasiticNetworkNet) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); EXPECT_EQ(pnet.net(), nullptr); } // Test ConcreteParasiticNetwork includesPinCaps TEST_F(StaParasiticsTest, ParasiticNetworkIncludesPinCaps) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet1(nullptr, false, network); + ConcreteParasiticNetwork pnet1(network); EXPECT_FALSE(pnet1.includesPinCaps()); - ConcreteParasiticNetwork pnet2(nullptr, true, network); + ConcreteParasiticNetwork pnet2(network); + pnet2.init(nullptr, true); EXPECT_TRUE(pnet2.includesPinCaps()); } // Test ConcreteParasiticNetwork addResistor/addCapacitor TEST_F(StaParasiticsTest, ParasiticNetworkAddDevices) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); ConcreteParasiticNode *node1 = new ConcreteParasiticNode(static_cast(nullptr), 1, false); ConcreteParasiticNode *node2 = new ConcreteParasiticNode(static_cast(nullptr), 2, false); @@ -1404,7 +1405,7 @@ TEST_F(StaParasiticsTest, ParasiticNetworkAddDevices) { // Test ConcreteParasiticNetwork findParasiticNode for pin (not found) TEST_F(StaParasiticsTest, ParasiticNetworkFindPinNodeNotFound) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); int dummy = 1; const Pin *pin = reinterpret_cast(&dummy); EXPECT_EQ(pnet.findParasiticNode(pin), nullptr); @@ -1414,7 +1415,7 @@ TEST_F(StaParasiticsTest, ParasiticNetworkFindPinNodeNotFound) { TEST_F(StaParasiticsTest, ConcreteParasiticsNetOnNetwork) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); // net() on isParasiticNetwork returns the net const Net *net = parasitics->net(static_cast(&pnet)); EXPECT_EQ(net, nullptr); // our network has nullptr net @@ -1424,7 +1425,8 @@ TEST_F(StaParasiticsTest, ConcreteParasiticsNetOnNetwork) { TEST_F(StaParasiticsTest, ConcreteParasiticsIncludesPinCaps) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, true, network); + ConcreteParasiticNetwork pnet(network); + pnet.init(nullptr, true); EXPECT_TRUE(parasitics->includesPinCaps(static_cast(&pnet))); } @@ -1432,7 +1434,7 @@ TEST_F(StaParasiticsTest, ConcreteParasiticsIncludesPinCaps) { TEST_F(StaParasiticsTest, ConcreteParasiticsNodes) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); ParasiticNodeSeq nodes = parasitics->nodes(static_cast(&pnet)); EXPECT_TRUE(nodes.empty()); } @@ -1441,7 +1443,7 @@ TEST_F(StaParasiticsTest, ConcreteParasiticsNodes) { TEST_F(StaParasiticsTest, ConcreteParasiticsResistors) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); ParasiticResistorSeq res = parasitics->resistors(static_cast(&pnet)); EXPECT_TRUE(res.empty()); } @@ -1450,7 +1452,7 @@ TEST_F(StaParasiticsTest, ConcreteParasiticsResistors) { TEST_F(StaParasiticsTest, ConcreteParasiticsCapacitors) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); ParasiticCapacitorSeq caps = parasitics->capacitors(static_cast(&pnet)); EXPECT_TRUE(caps.empty()); } @@ -1459,7 +1461,7 @@ TEST_F(StaParasiticsTest, ConcreteParasiticsCapacitors) { TEST_F(StaParasiticsTest, FindParasiticNodeNetId) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); // Nothing in the network, so findParasiticNode should return nullptr EXPECT_EQ(parasitics->findParasiticNode(static_cast(&pnet), static_cast(nullptr), 0, network), nullptr); @@ -1469,7 +1471,7 @@ TEST_F(StaParasiticsTest, FindParasiticNodeNetId) { TEST_F(StaParasiticsTest, FindParasiticNodePin) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); EXPECT_EQ(parasitics->findParasiticNode(static_cast(&pnet), static_cast(nullptr)), nullptr); } @@ -1478,7 +1480,7 @@ TEST_F(StaParasiticsTest, FindParasiticNodePin) { TEST_F(StaParasiticsTest, MakeResistorCapacitorApi) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); Parasitic *ppnet = static_cast(&pnet); // Create nodes first using direct construction @@ -1510,7 +1512,7 @@ TEST_F(StaParasiticsTest, MakeResistorCapacitorApi) { TEST_F(StaParasiticsTest, ParasiticNodeResistorMapApi) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); Parasitic *ppnet = static_cast(&pnet); ConcreteParasiticNode *node1 = new ConcreteParasiticNode(static_cast(nullptr), 1, false); @@ -1537,7 +1539,7 @@ TEST_F(StaParasiticsTest, ParasiticNodeResistorMapApi) { TEST_F(StaParasiticsTest, ParasiticNodeCapacitorMapApi) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); Parasitic *ppnet = static_cast(&pnet); ConcreteParasiticNode *node1 = new ConcreteParasiticNode(static_cast(nullptr), 1, false); @@ -1583,7 +1585,7 @@ TEST_F(StaParasiticsTest, ReportPiElmore) { TEST_F(StaParasiticsTest, NetworkDisconnectPin) { ASSERT_NO_THROW(( [&](){ const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); // disconnectPin with nullptr should not crash pnet.disconnectPin(nullptr, nullptr, network); @@ -1627,7 +1629,7 @@ TEST_F(StaParasiticsTest, DeletePinBefore) { // Test ConcreteParasiticNetwork capacitance with grounded caps and coupling caps TEST_F(StaParasiticsTest, ParasiticNetworkCapacitanceMixed) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); // Create nodes with grounded capacitance ConcreteParasiticNode *node1 = new ConcreteParasiticNode(static_cast(nullptr), 1, false); @@ -1739,7 +1741,7 @@ class ReduceParasiticsTest : public ::testing::Test { // Test reduceToPiElmore returns nullptr when drvr_node not found TEST_F(ReduceParasiticsTest, ReduceNoDrvrNode) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); // No driver node in the network, so reduction returns nullptr Parasitic *result = reduceToPiElmore( @@ -1760,7 +1762,7 @@ TEST_F(ReduceParasiticsTest, ReduceNoDrvrNode) { // Test reduceToPiPoleResidue2 returns nullptr when drvr_node not found TEST_F(ReduceParasiticsTest, ReducePoleResidue2NoDrvrNode) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); Parasitic *result = reduceToPiPoleResidue2( static_cast(&pnet), @@ -1827,7 +1829,7 @@ TEST_F(ReduceParasiticsTest, DeleteParasiticsPin) { TEST_F(ReduceParasiticsTest, FindNodePinBase) { Parasitics *parasitics = sta_->findParasitics("default"); const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); // findNode with nullptr pin on empty network (suppress deprecation warning) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" @@ -2348,7 +2350,7 @@ TEST_F(StaParasiticsTest, ConcretePiPoleResidueD0Destructor) { // Covers: ConcreteParasiticNetwork::nodes, resistors, capacitors, capacitance TEST_F(StaParasiticsTest, ParasiticNetworkCreation) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); EXPECT_TRUE(pnet.isParasiticNetwork()); EXPECT_FALSE(pnet.includesPinCaps()); EXPECT_EQ(pnet.net(), nullptr); @@ -2362,7 +2364,8 @@ TEST_F(StaParasiticsTest, ParasiticNetworkCreation) { // Covers: ConcreteParasiticNetwork constructor with includes_pin_caps=true TEST_F(StaParasiticsTest, ParasiticNetworkIncludesPinCaps2) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, true, network); + ConcreteParasiticNetwork pnet(network); + pnet.init(nullptr, true); EXPECT_TRUE(pnet.includesPinCaps()); } @@ -2370,7 +2373,7 @@ TEST_F(StaParasiticsTest, ParasiticNetworkIncludesPinCaps2) { // Covers: ConcreteParasiticNetwork::findParasiticNode TEST_F(StaParasiticsTest, ParasiticNetworkFindNodeMissing) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); ConcreteParasiticNode *node = pnet.findParasiticNode(static_cast(nullptr)); EXPECT_EQ(node, nullptr); } @@ -2379,7 +2382,7 @@ TEST_F(StaParasiticsTest, ParasiticNetworkFindNodeMissing) { // Covers: ConcreteParasiticNetwork::addResistor TEST_F(StaParasiticsTest, ParasiticNetworkAddResistorStandalone) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); ConcreteParasiticNode node1(static_cast(nullptr), 1, false); ConcreteParasiticNode node2(static_cast(nullptr), 2, false); ConcreteParasiticResistor *res = new ConcreteParasiticResistor(0, 100.0f, &node1, &node2); @@ -2391,7 +2394,7 @@ TEST_F(StaParasiticsTest, ParasiticNetworkAddResistorStandalone) { // Covers: ConcreteParasiticNetwork::addCapacitor TEST_F(StaParasiticsTest, ParasiticNetworkAddCapacitorStandalone) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); ConcreteParasiticNode node1(static_cast(nullptr), 1, false); ConcreteParasiticNode node2(static_cast(nullptr), 2, false); ConcreteParasiticCapacitor *cap = new ConcreteParasiticCapacitor(0, 5e-15f, &node1, &node2); @@ -2411,7 +2414,7 @@ TEST_F(StaParasiticsTest, ParasiticsAndSceneAvailable) { // Covers: ConcreteParasiticNetwork::resistors, ConcreteParasiticNetwork::capacitors TEST_F(StaParasiticsTest, ParasiticNetworkEmptyLists) { const Network *network = sta_->network(); - ConcreteParasiticNetwork pnet(nullptr, false, network); + ConcreteParasiticNetwork pnet(network); EXPECT_TRUE(pnet.resistors().empty()); EXPECT_TRUE(pnet.capacitors().empty()); } From 9d588bc90ba7797981b65902e8339e86b6f60598 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Wed, 7 Oct 2026 09:27:08 -0700 Subject: [PATCH 29/52] GraphDelayCalc::zeroDrvrSlew zero wire delays Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 7b590f32..8703890e 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -1539,6 +1539,15 @@ GraphDelayCalc::zeroDrvrSlew(Vertex *drvr_vertex, // Zero drvr slew. if (!drvr_vertex->slewAnnotated(rf, min_max)) graph_->setSlew(drvr_vertex, rf, ap_index, delay_zero); + + // Zero wire delays. + VertexOutEdgeIterator edge_iter(drvr_vertex, graph_); + while (edge_iter.hasNext()) { + Edge *wire_edge = edge_iter.next(); + if (wire_edge->isWire() + && !graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) + graph_->setWireArcDelay(wire_edge, rf, ap_index, delay_zero); + } } } } From 99ffdffddc658f59a2246695df977ffe08edabf1 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:50:01 -0700 Subject: [PATCH 30/52] dcalc zero uninited load slews Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 85 ++++++++++++++++++++++------------- include/sta/GraphDelayCalc.hh | 13 ++++-- 2 files changed, 63 insertions(+), 35 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index 8703890e..d4429c82 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -701,20 +701,20 @@ GraphDelayCalc::findVertexDelay(Vertex *vertex, else if (network_->isLeaf(pin) && vertex->isDriver(network_)) { LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(vertex); - DrvrLoadSlews load_slews_prev; + DrvrLoadSlews load_slew_prev; if (delays_exist_) - load_slews_prev = loadSlews(load_pin_index_map); + load_slew_prev = loadSlews(load_pin_index_map); findDriverDelays(vertex, arc_delay_calc, load_pin_index_map); if (network_->direction(pin)->isInternal()) enqueueCheckEdges(vertex); graph_->visitFanouts(vertex, search_non_latch_pred_, - [this, &load_slews_prev, &load_pin_index_map] + [this, &load_slew_prev, &load_pin_index_map] (Vertex *fanout) { // Enqueue adjacent vertices even if the load slew // did not change when non-incremental to stride // past annotations. if (!delays_exist_ - || loadSlewChanged(fanout, load_slews_prev, + || loadSlewChanged(fanout, load_slew_prev, load_pin_index_map)) { iter_->enqueue(fanout); } @@ -804,16 +804,20 @@ GraphDelayCalc::findDriverDelays(Vertex *drvr_vertex, LoadPinIndexMap &load_pin_index_map) { MultiDrvrNet *multi_drvr = findMultiDrvrNet(drvr_vertex); + RiseFallExists load_slew_set = {false, false}; if (multi_drvr == nullptr) { initLoadSlews(drvr_vertex); - findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc, load_pin_index_map); + findDriverDelays1(drvr_vertex, multi_drvr, arc_delay_calc, + load_pin_index_map, load_slew_set); + zeroUnsetLoadSlews(load_pin_index_map, load_slew_set); } else if (drvr_vertex == multi_drvr->dcalcDrvr()) { initLoadSlews(drvr_vertex); for (Vertex *drvr : multi_drvr->drvrs()) - findDriverDelays1(drvr, multi_drvr, arc_delay_calc, load_pin_index_map); + findDriverDelays1(drvr, multi_drvr, arc_delay_calc, load_pin_index_map, + load_slew_set); + zeroUnsetLoadSlews(load_pin_index_map, load_slew_set); } - arc_delay_calc->finishDrvrPin(); } MultiDrvrNet * @@ -952,31 +956,31 @@ GraphDelayCalc::initLoadSlews(Vertex *drvr_vertex) } } -bool +void GraphDelayCalc::findDriverDelays1(Vertex *drvr_vertex, MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, - LoadPinIndexMap &load_pin_index_map) + LoadPinIndexMap &load_pin_index_map, + RiseFallExists &load_slew_set) { initSlew(drvr_vertex); initWireDelays(drvr_vertex); bool delay_changed = false; - std::array delay_exists = {false, false}; + RiseFallExists drvr_slew_set = {false, false}; VertexInEdgeIterator edge_iter(drvr_vertex, graph_); while (edge_iter.hasNext()) { Edge *edge = edge_iter.next(); if (!edge->role()->isLatchDtoQ()) delay_changed |= findDriverEdgeDelays(drvr_vertex, multi_drvr, edge, arc_delay_calc, load_pin_index_map, - delay_exists); + drvr_slew_set, load_slew_set); } for (const RiseFall *rf : RiseFall::range()) { - if (!delay_exists[rf->index()]) + if (!drvr_slew_set[rf->index()]) zeroDrvrSlew(drvr_vertex, rf); } if (delay_changed && observer_) observer_->delayChangedTo(drvr_vertex); - return delay_changed; } // Init slews to zero on root vertices that are not inputs, such as @@ -1003,11 +1007,12 @@ GraphDelayCalc::findLatchEdgeDelays(Edge *edge) Instance *drvr_inst = network_->instance(drvr_pin); debugPrint(debug_, "delay_calc", 2, "find latch D->Q {}", sdc_network_->pathName(drvr_inst)); - std::array delay_exists = {false, false}; LoadPinIndexMap load_pin_index_map = makeLoadPinIndexMap(drvr_vertex); + RiseFallExists drvr_slew_set = {false, false}; + RiseFallExists load_slew_set = {false, false}; bool delay_changed = findDriverEdgeDelays(drvr_vertex, nullptr, edge, arc_delay_calc_, load_pin_index_map, - delay_exists); + drvr_slew_set, load_slew_set); if (delay_changed && observer_) observer_->delayChangedTo(drvr_vertex); } @@ -1019,12 +1024,12 @@ GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map, // Return value. - std::array &delay_exists) + RiseFallExists &drvr_slew_set, + RiseFallExists &load_slew_set) { Vertex *from_vertex = edge->from(graph_); const TimingArcSet *arc_set = edge->timingArcSet(); bool delay_changed = false; - for (Scene *scene : scenes_) { const Mode *mode = scene->mode(); if (search_pred_->searchFrom(from_vertex, mode) @@ -1034,7 +1039,9 @@ GraphDelayCalc::findDriverEdgeDelays(Vertex *drvr_vertex, delay_changed |= findDriverArcDelays(drvr_vertex, multi_drvr, edge, arc, scene, min_max, arc_delay_calc, load_pin_index_map); - delay_exists[arc->toEdge()->asRiseFall()->index()] = true; + size_t drvr_rf_index = arc->toEdge()->asRiseFall()->index(); + drvr_slew_set[drvr_rf_index] = true; + load_slew_set[drvr_rf_index] = true; } } } @@ -1103,14 +1110,8 @@ GraphDelayCalc::findDriverArcDelays(Vertex *drvr_vertex, load_cap, parasitic, load_pin_index_map, scene, min_max); - if (multi_drvr) { - LockGuard lock(multi_drvr_lock_); - delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, - load_pin_index_map, scene, min_max); - } - else - delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, - load_pin_index_map, scene, min_max); + delay_changed |= annotateDelaysSlews(edge, arc, dcalc_result, + load_pin_index_map, scene, min_max); } arc_delay_calc->finishDrvrPin(); } @@ -1529,6 +1530,28 @@ GraphDelayCalc::initSlew(Vertex *vertex) } } +void +GraphDelayCalc::zeroUnsetLoadSlews(LoadPinIndexMap &load_pin_index_map, + RiseFallExists &load_slew_set) +{ + if (!load_slew_set[RiseFall::riseIndex()] + || !load_slew_set[RiseFall::fallIndex()]) { + for (auto [load_pin, index] : load_pin_index_map) { + Vertex *load_vertex = graph_->pinLoadVertex(load_pin); + for (Scene *scene : scenes_) { + for (const MinMax *min_max : MinMax::range()) { + DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); + for (const RiseFall *rf : RiseFall::range()) { + if (!load_vertex->slewAnnotated(rf, min_max) + && !load_slew_set[rf->index()]) + graph_->setSlew(load_vertex, rf, ap_index, delay_zero); + } + } + } + } + } +} + void GraphDelayCalc::zeroDrvrSlew(Vertex *drvr_vertex, const RiseFall *rf) @@ -1564,14 +1587,14 @@ GraphDelayCalc::initWireDelays(Vertex *drvr_vertex) DcalcAPIndex ap_index = scene->dcalcAnalysisPtIndex(min_max); Delay delay_init_value(min_max->initValue()); - for (const RiseFall *rf : RiseFall::range()) { - if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) - graph_->setWireArcDelay(wire_edge, rf, ap_index, delay_init_value); - } + for (const RiseFall *rf : RiseFall::range()) { + if (!graph_->wireDelayAnnotated(wire_edge, rf, ap_index)) + graph_->setWireArcDelay(wire_edge, rf, ap_index, delay_init_value); + } + } } } } - } } Slew diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index 778765aa..dac89def 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -46,6 +46,7 @@ class SearchPred; using MultiDrvrNetMap = std::map; using DrvrLoadSlews = std::vector; +using RiseFallExists = std::array; // This class traverses the graph calling the arc delay calculator and // annotating delays on graph edges. @@ -196,18 +197,20 @@ protected: MultiDrvrNet *makeMultiDrvrNet(Vertex *drvr_vertex); bool hasMultiDrvrs(Vertex *drvr_vertex); Vertex *firstLoad(Vertex *drvr_vertex); - bool findDriverDelays1(Vertex *drvr_vertex, + void findDriverDelays1(Vertex *drvr_vertex, MultiDrvrNet *multi_drvr, ArcDelayCalc *arc_delay_calc, - LoadPinIndexMap &load_pin_index_map); + LoadPinIndexMap &load_pin_index_map, + RiseFallExists &load_slew_set); void initLoadSlews(Vertex *drvr_vertex); bool findDriverEdgeDelays(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map, - // Return value. - std::array &delay_exists); + // Return values. + RiseFallExists &drvr_slew_set, + RiseFallExists &load_slew_set); bool findDriverArcDelays(Vertex *drvr_vertex, const MultiDrvrNet *multi_drvr, Edge *edge, @@ -232,6 +235,8 @@ protected: void initRootSlews(Vertex *vertex); void zeroDrvrSlew(Vertex *drvr_vertex, const RiseFall *rf); + void zeroUnsetLoadSlews(LoadPinIndexMap &load_pin_index_map, + RiseFallExists &load_slew_set); void findVertexDelay(Vertex *vertex, ArcDelayCalc *arc_delay_calc); DrvrLoadSlews loadSlews(LoadPinIndexMap &load_pin_index_map); From 6d8f13f2892202351645ab0ffee0e3a9e7bc1d36 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Thu, 8 Oct 2026 13:59:52 -0700 Subject: [PATCH 31/52] ensure parasitics Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- include/sta/Sdc.hh | 1 + parasitics/ConcreteParasitics.cc | 24 +++++++++++++++++++++--- parasitics/ConcreteParasitics.hh | 2 ++ parasitics/SpefReader.cc | 6 ++---- sdc/Sdc.cc | 7 +++++++ search/Sta.cc | 10 ++++++++-- 6 files changed, 41 insertions(+), 9 deletions(-) diff --git a/include/sta/Sdc.hh b/include/sta/Sdc.hh index b5fda274..3ee08090 100644 --- a/include/sta/Sdc.hh +++ b/include/sta/Sdc.hh @@ -814,6 +814,7 @@ public: FilterPath *filter() const { return filter_; } void deleteFilter(); + bool wireloadModelsEnabled() const; Wireload *wireload(const MinMax *min_max) const; void setWireload(Wireload *wireload, const MinMaxAll *min_max); diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 011eb272..21489c4f 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -944,6 +944,18 @@ ConcreteParasitics::ensureParasitics(const Pin *drvr_pin) drvr_parasitic_map_.try_emplace(drvr_pin); } +void +ConcreteParasitics::ensureReducedParasitics(const Net *net) +{ + NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net); + while (pin_iter->hasNext()) { + const Pin *pin = pin_iter->next(); + if (network_->isDriver(pin)) + drvr_parasitic_map_.try_emplace(pin); + } + delete pin_iter; +} + Parasitic * ConcreteParasitics::findPiElmore(const Pin *drvr_pin, const RiseFall *rf, @@ -1205,9 +1217,15 @@ Parasitic * ConcreteParasitics::makeParasiticNetwork(const Net *net, bool includes_pin_caps) { - ConcreteParasiticNetwork ¶sitic = parasitic_network_map_.find(net)->second; - parasitic.init(net, includes_pin_caps); - return ¶sitic; + ConcreteParasiticNetwork *parasitic = nullptr; + auto itr = parasitic_network_map_.find(net); + if (itr == parasitic_network_map_.end()) + parasitic = ¶sitic_network_map_.try_emplace(net, network_).first->second; + else + parasitic = &itr->second; + parasitic->init(net, includes_pin_caps); + ensureReducedParasitics(net); + return parasitic; } void diff --git a/parasitics/ConcreteParasitics.hh b/parasitics/ConcreteParasitics.hh index e76f2fae..6702ea67 100644 --- a/parasitics/ConcreteParasitics.hh +++ b/parasitics/ConcreteParasitics.hh @@ -191,6 +191,8 @@ protected: Parasitic *ensureRspf(const Pin *drvr_pin); void makeAnalysisPtAfter(); void deleteReducedParasitics(const Pin *pin); + // Make driver map entries for reduced parastitics. + void ensureReducedParasitics(const Net *net); std::string name_; std::string filename_; diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 6f270b00..0c5d87ab 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -92,8 +92,6 @@ SpefReader::SpefReader(std::string_view filename, bool SpefReader::read() { - parasitics_->ensureParasitics(); - bool success; gzstream::igzstream stream(std::string(filename_).c_str()); if (stream.is_open()) { @@ -389,8 +387,8 @@ SpefReader::dspfBegin(Net *net, delete term_iter; parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner); if (parasitic_ == nullptr) - parasitic_ = - parasitics_->makeParasiticNetwork(parasitic_owner, pin_cap_included_); + parasitic_ = parasitics_->makeParasiticNetwork(parasitic_owner, + pin_cap_included_); } net_ = net; } diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index 812e4561..33d84d41 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -5703,6 +5703,13 @@ Sdc::groupPathsTo(const ExceptionPathSet *to_exceptions, //////////////////////////////////////////////////////////////// +bool +Sdc::wireloadModelsEnabled() const +{ + return wireload_[MinMax::minIndex()] + || wireload_[MinMax::maxIndex()]; +} + Wireload * Sdc::wireload(const MinMax *min_max) const { diff --git a/search/Sta.cc b/search/Sta.cc index 112c92e1..1051cb05 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -1064,8 +1064,6 @@ Sta::setWireload(Wireload *wireload, Sdc *sdc) { sdc->setWireload(wireload, min_max); - for (auto &[name, parasitics] : parasitics_name_map_) - parasitics->ensureParasitics(); delaysInvalid(); } @@ -3706,9 +3704,17 @@ void Sta::delayCalcPreamble() { ensureLevelized(); + bool wireloads_enabled = false; for (Mode *mode : modes_) { mode->sim()->ensureConstantsPropagated(); mode->clkNetwork()->ensureClkNetwork(); + if (mode->sdc()->wireloadModelsEnabled()) + wireloads_enabled = true; + } + if (wireloads_enabled) { + // Make parasitic maps for wireload parasitics so avoid locking them. + for (auto &[name, parasitics] : parasitics_name_map_) + parasitics->ensureParasitics(); } } From becb329406aaa1360ceb9ffa6e4226952cc268f6 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Thu, 8 Oct 2026 16:09:33 -0700 Subject: [PATCH 32/52] hierarchical spef annotation ala pr #524 Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- parasitics/Parasitics.cc | 6 ++++-- parasitics/SpefReader.cc | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/parasitics/Parasitics.cc b/parasitics/Parasitics.cc index a31eb5f7..9a385878 100644 --- a/parasitics/Parasitics.cc +++ b/parasitics/Parasitics.cc @@ -105,8 +105,10 @@ Parasitics::findParasiticNet(const Pin *pin) const // Use the net connected to the pin's terminal. if (net == nullptr && network_->isTopLevelPort(pin)) { Term *term = network_->term(pin); - if (term) - return network_->net(term); + if (term) { + Net *term_net = network_->net(term); + return term_net ? network_->highestConnectedNet(term_net) : nullptr; + } else return nullptr; } diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 0c5d87ab..475f504d 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -377,18 +377,19 @@ SpefReader::dspfBegin(Net *net, parasitic_ = parasitics_->makeParasiticNetwork(net, pin_cap_included_); } else { - Net *parasitic_owner = net; + const Net *owner = net; NetTermIterator *term_iter = network_->termIterator(net); if (term_iter->hasNext()) { Term *term = term_iter->next(); Pin *hpin = network_->pin(term); - parasitic_owner = network_->net(hpin); + owner = network_->net(hpin); } delete term_iter; - parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner); + // The highest connected net owns the parasitic network. + owner = network_->highestConnectedNet(const_cast(owner)); + parasitic_ = parasitics_->findParasiticNetwork(owner); if (parasitic_ == nullptr) - parasitic_ = parasitics_->makeParasiticNetwork(parasitic_owner, - pin_cap_included_); + parasitic_ = parasitics_->makeParasiticNetwork(owner, pin_cap_included_); } net_ = net; } From ed4450b80da6b275251f9354fba9b35c10081814 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Thu, 8 Oct 2026 16:52:50 -0700 Subject: [PATCH 33/52] Network::highestConnectedNet const Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- include/sta/Network.hh | 6 +++--- network/Network.cc | 12 ++++++------ parasitics/ConcreteParasitics.cc | 4 ++-- parasitics/SpefReader.cc | 2 +- search/ReportPath.cc | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/sta/Network.hh b/include/sta/Network.hh index 89d29722..55dee3eb 100644 --- a/include/sta/Network.hh +++ b/include/sta/Network.hh @@ -389,9 +389,9 @@ public: // Is net1 connected to net2 anywhere in the hierarchy? virtual bool isConnected(const Net *net1, const Net *net2) const; - virtual Net *highestNetAbove(Net *net) const; - virtual const Net *highestConnectedNet(Net *net) const; - virtual void connectedNets(Net *net, + virtual const Net *highestNetAbove(const Net *net) const; + virtual const Net *highestConnectedNet(const Net *net) const; + virtual void connectedNets(const Net *net, NetSet *nets) const; virtual void connectedNets(const Pin *pin, NetSet *nets) const; diff --git a/network/Network.cc b/network/Network.cc index 373d52b7..980e65a2 100644 --- a/network/Network.cc +++ b/network/Network.cc @@ -480,17 +480,17 @@ Network::pathNameCmp(const Net *net1, return inst_cmp; } -Net * -Network::highestNetAbove(Net *net) const +const Net * +Network::highestNetAbove(const Net *net) const { - Net *highest_net = net; + const Net *highest_net = net; // Search up from net terminals. NetTermIterator *term_iter = termIterator(net); while (term_iter->hasNext()) { Term *term = term_iter->next(); Pin *above_pin = pin(term); if (above_pin) { - Net *above_net = this->net(above_pin); + const Net *above_net = this->net(above_pin); if (above_net) { highest_net = highestNetAbove(above_net); break; @@ -502,7 +502,7 @@ Network::highestNetAbove(Net *net) const } const Net * -Network::highestConnectedNet(Net *net) const +Network::highestConnectedNet(const Net *net) const { NetSet nets(this); connectedNets(net, &nets); @@ -521,7 +521,7 @@ Network::highestConnectedNet(Net *net) const } void -Network::connectedNets(Net *net, +Network::connectedNets(const Net *net, NetSet *nets) const { if (!nets->contains(net)) { diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 21489c4f..54d8f8db 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -623,7 +623,7 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, NetIdPair net_id(net, id); auto id_node = sub_nodes_.find(net_id); if (id_node == sub_nodes_.end()) { - Net *net1 = network->highestNetAbove(const_cast(net)); + const Net *net1 = network->highestNetAbove(const_cast(net)); node = new ConcreteParasiticNode(net, id, network->highestNetAbove(net1) != net_); sub_nodes_[net_id] = node; if (net == net_) @@ -641,7 +641,7 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Pin *pin, ConcreteParasiticNode *node; auto pin_node = pin_nodes_.find(pin); if (pin_node == pin_nodes_.end()) { - Net *net = network->net(pin); + const Net *net = network->net(pin); // Pins on the top level instance may not have nets. // Use the net connected to the pin's terminal. if (net == nullptr && network->isTopLevelPort(pin)) { diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 475f504d..4a0a488d 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -386,7 +386,7 @@ SpefReader::dspfBegin(Net *net, } delete term_iter; // The highest connected net owns the parasitic network. - owner = network_->highestConnectedNet(const_cast(owner)); + owner = network_->highestConnectedNet(owner); parasitic_ = parasitics_->findParasiticNetwork(owner); if (parasitic_ == nullptr) parasitic_ = parasitics_->makeParasiticNetwork(owner, pin_cap_included_); diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 0fc53e73..81155190 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -2999,9 +2999,9 @@ ReportPath::descriptionNet(const Pin *pin) const if (network_->isTopLevelPort(pin)) return sta::format("{} (net)", cmd_network_->pathName(pin)); else { - Net *net = network_->net(pin); + const Net *net = network_->net(pin); if (net) { - Net *highest_net = network_->highestNetAbove(net); + const Net *highest_net = network_->highestNetAbove(net); std::string net_name = cmd_network_->pathName(highest_net); return sta::format("{} (net)", net_name); } From 4fb393c6283f8787612e76782550c584e7029b7a Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Thu, 8 Oct 2026 16:54:26 -0700 Subject: [PATCH 34/52] Network::highestConnectedNet const Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- parasitics/ConcreteParasitics.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 54d8f8db..88758541 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -623,7 +623,7 @@ ConcreteParasiticNetwork::ensureParasiticNode(const Net *net, NetIdPair net_id(net, id); auto id_node = sub_nodes_.find(net_id); if (id_node == sub_nodes_.end()) { - const Net *net1 = network->highestNetAbove(const_cast(net)); + const Net *net1 = network->highestNetAbove(net); node = new ConcreteParasiticNode(net, id, network->highestNetAbove(net1) != net_); sub_nodes_[net_id] = node; if (net == net_) From 7b261909c1730d963252142e136daaa0199e333c Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:38:35 -0700 Subject: [PATCH 35/52] read_spef -path -reduce Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- parasitics/ConcreteParasitics.cc | 6 +++++- parasitics/SpefReader.cc | 37 ++++++++++++++++++++------------ parasitics/SpefReaderPvt.hh | 1 + 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/parasitics/ConcreteParasitics.cc b/parasitics/ConcreteParasitics.cc index 88758541..e7d18913 100644 --- a/parasitics/ConcreteParasitics.cc +++ b/parasitics/ConcreteParasitics.cc @@ -1231,7 +1231,11 @@ ConcreteParasitics::makeParasiticNetwork(const Net *net, void ConcreteParasitics::deleteParasiticNetwork(const Net *net) { - parasitic_network_map_.find(net)->second.init(net, false); + auto itr = parasitic_network_map_.find(net); + if (itr != parasitic_network_map_.end()) { + ConcreteParasiticNetwork ¶rsitic = itr->second; + pararsitic.init(net, false); + } } const Net * diff --git a/parasitics/SpefReader.cc b/parasitics/SpefReader.cc index 4a0a488d..eb345b06 100644 --- a/parasitics/SpefReader.cc +++ b/parasitics/SpefReader.cc @@ -377,19 +377,10 @@ SpefReader::dspfBegin(Net *net, parasitic_ = parasitics_->makeParasiticNetwork(net, pin_cap_included_); } else { - const Net *owner = net; - NetTermIterator *term_iter = network_->termIterator(net); - if (term_iter->hasNext()) { - Term *term = term_iter->next(); - Pin *hpin = network_->pin(term); - owner = network_->net(hpin); - } - delete term_iter; - // The highest connected net owns the parasitic network. - owner = network_->highestConnectedNet(owner); - parasitic_ = parasitics_->findParasiticNetwork(owner); + const Net *dspef_net = dspefNet(net); + parasitic_ = parasitics_->findParasiticNetwork(dspef_net); if (parasitic_ == nullptr) - parasitic_ = parasitics_->makeParasiticNetwork(owner, pin_cap_included_); + parasitic_ = parasitics_->makeParasiticNetwork(dspef_net, pin_cap_included_); } net_ = net; } @@ -400,12 +391,30 @@ SpefReader::dspfBegin(Net *net, delete total_cap; } +const Net * +SpefReader::dspefNet(const Net *net) +{ + const Net *dspef_net = net; + NetTermIterator *term_iter = network_->termIterator(net); + if (term_iter->hasNext()) { + Term *term = term_iter->next(); + Pin *hpin = network_->pin(term); + const Net *term_net = network_->net(hpin); + if (term_net) + dspef_net = term_net; + } + delete term_iter; + // The highest connected net owns the parasitic network. + return network_->highestConnectedNet(dspef_net); +} + void SpefReader::dspfFinish() { if (parasitic_ && reduce_) { - arc_delay_calc_->reduceParasitic(parasitic_, net_, scene_, min_max_); - parasitics_->deleteParasiticNetwork(net_); + const Net *dspef_net = dspefNet(net_); + arc_delay_calc_->reduceParasitic(parasitic_, dspef_net, scene_, min_max_); + parasitics_->deleteParasiticNetwork(dspef_net); } parasitic_ = nullptr; net_ = nullptr; diff --git a/parasitics/SpefReaderPvt.hh b/parasitics/SpefReaderPvt.hh index 4b1b90ff..4adbd686 100644 --- a/parasitics/SpefReaderPvt.hh +++ b/parasitics/SpefReaderPvt.hh @@ -123,6 +123,7 @@ private: Instance *findInstanceRelative(std::string_view name); ParasiticNode *findParasiticNode(std::string_view name, bool local_only); + const Net *dspefNet(const Net *net); std::string_view filename_; SpefScanner *scanner_; From 8095301c7efc1e3c9eef692a67bd590a19da0374 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Fri, 9 Oct 2026 17:08:32 -0700 Subject: [PATCH 36/52] untabify Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- dcalc/GraphDelayCalc.cc | 38 +++++++++--------- include/sta/GraphDelayCalc.hh | 72 +++++++++++++++++------------------ 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/dcalc/GraphDelayCalc.cc b/dcalc/GraphDelayCalc.cc index d4429c82..beea2b23 100644 --- a/dcalc/GraphDelayCalc.cc +++ b/dcalc/GraphDelayCalc.cc @@ -61,7 +61,7 @@ static const Slew default_slew = 0.0; static bool isLeafDriver(const Pin *pin, - const Network *network); + const Network *network); //////////////////////////////////////////////////////////////// @@ -93,7 +93,7 @@ DcalcPred::searchFrom(const Vertex *from_vertex, Net *net = network->net(from_pin); return !(sdc->isDisabledConstraint(from_pin) || sim->isConstant(from_vertex) - || (net && (network->isPower(net) + || (net && (network->isPower(net) || network->isGround(net)))); } @@ -106,7 +106,7 @@ DcalcPred::searchThru(Edge *edge, const TimingRole *role = edge->role(); return !(role->isTimingCheck() || (role == TimingRole::regSetClr() - && !variables->presetClrArcsEnabled()) + && !variables->presetClrArcsEnabled()) || edge->isDisabledLoop() || sdc->isDisabledConstraint(edge) || sdc->isDisabledCondDefault(edge) @@ -229,17 +229,17 @@ GraphDelayCalc::delayInvalid(const Pin *pin) if (network_->isHierarchical(pin)) { EdgesThruHierPinIterator edge_iter(pin, network_, graph_); while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - delayInvalid(edge->from(graph_)); + Edge *edge = edge_iter.next(); + delayInvalid(edge->from(graph_)); } } else { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); if (vertex) - delayInvalid(vertex); + delayInvalid(vertex); if (bidirect_drvr_vertex) - delayInvalid(bidirect_drvr_vertex); + delayInvalid(bidirect_drvr_vertex); } } } @@ -882,7 +882,7 @@ GraphDelayCalc::firstLoad(Vertex *drvr_vertex) static bool isLeafDriver(const Pin *pin, - const Network *network) + const Network *network) { PortDirection *dir = network->direction(pin); const Instance *inst = network->instance(pin); @@ -1463,7 +1463,7 @@ GraphDelayCalc::parasiticLoad(const Pin *drvr_pin, // PiModel includes both pin and external caps. float parasitic_cap = parasitics->capacitance(parasitic); if (parasitic_cap >= pin_cap) - wire_cap = parasitic_cap - pin_cap; + wire_cap = parasitic_cap - pin_cap; else { wire_cap = 0.0; // Ignore parasitic if pin cap is greater. @@ -1507,12 +1507,12 @@ GraphDelayCalc::netCaps(const Pin *drvr_pin, { if (multi_drvr) multi_drvr->netCaps(rf, scene, min_max, - pin_cap, wire_cap, fanout, has_net_load); + pin_cap, wire_cap, fanout, has_net_load); else { const Sdc *sdc = scene->sdc(); // Find pin and external pin/wire capacitance. sdc->connectedCap(drvr_pin, rf, scene, min_max, - pin_cap, wire_cap, fanout, has_net_load); + pin_cap, wire_cap, fanout, has_net_load); } } @@ -1766,9 +1766,9 @@ GraphDelayCalc::reportDelayCalc(const Edge *edge, void GraphDelayCalc::minPeriod(const Pin *pin, const Scene *scene, - // Return values. - float &min_period, - bool &exists) + // Return values. + float &min_period, + bool &exists) { exists = false; const MinMax *min_max = MinMax::max(); @@ -1821,11 +1821,11 @@ void MultiDrvrNet::netCaps(const RiseFall *drvr_rf, const Scene *scene, const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load) const + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load) const { int index = scene->dcalcAnalysisPtIndex(min_max) * RiseFall::index_count + drvr_rf->index(); diff --git a/include/sta/GraphDelayCalc.hh b/include/sta/GraphDelayCalc.hh index dac89def..456591d9 100644 --- a/include/sta/GraphDelayCalc.hh +++ b/include/sta/GraphDelayCalc.hh @@ -129,17 +129,17 @@ public: // Liberty port min_period attribute. void minPeriod(const Pin *pin, const Scene *scene, - // Return values. - float &min_period, - bool &exists); + // Return values. + float &min_period, + bool &exists); Slew edgeFromSlew(const Vertex *from_vertex, - const RiseFall *from_rf, - const Edge *edge, + const RiseFall *from_rf, + const Edge *edge, const Scene *scene, const MinMax *min_max); Slew edgeFromSlew(const Vertex *from_vertex, - const RiseFall *from_rf, + const RiseFall *from_rf, const TimingRole *role, const Scene *scene, const MinMax *min_max); @@ -149,48 +149,48 @@ protected: void seedInvalidDelays(); void initSlew(Vertex *vertex); void seedRootSlew(Vertex *vertex, - ArcDelayCalc *arc_delay_calc); + ArcDelayCalc *arc_delay_calc); void seedRootSlews(); void seedDrvrSlew(Vertex *vertex, - ArcDelayCalc *arc_delay_calc); + ArcDelayCalc *arc_delay_calc); void seedNoDrvrSlew(Vertex *drvr_vertex, - const Pin *drvr_pin, - const RiseFall *rf, + const Pin *drvr_pin, + const RiseFall *rf, const Scene *scene, const MinMax *min_max, - ArcDelayCalc *arc_delay_calc); + ArcDelayCalc *arc_delay_calc); void seedNoDrvrCellSlew(Vertex *drvr_vertex, - const Pin *drvr_pin, - const RiseFall *rf, - const InputDrive *drive, + const Pin *drvr_pin, + const RiseFall *rf, + const InputDrive *drive, const Scene *scene, const MinMax *min_max, - ArcDelayCalc *arc_delay_calc); + ArcDelayCalc *arc_delay_calc); void seedLoadSlew(Vertex *vertex); void setInputPortWireDelays(Vertex *vertex); void findInputDriverDelay(const LibertyCell *drvr_cell, - const Pin *drvr_pin, - Vertex *drvr_vertex, - const RiseFall *rf, - const LibertyPort *from_port, + const Pin *drvr_pin, + Vertex *drvr_vertex, + const RiseFall *rf, + const LibertyPort *from_port, const DriveCellSlews *from_slews, - const LibertyPort *to_port, + const LibertyPort *to_port, const Scene *scene, const MinMax *min_max, ArcDelayCalc *arc_delay_calc); LibertyPort *driveCellDefaultFromPort(const LibertyCell *cell, - const LibertyPort *to_port); + const LibertyPort *to_port); int findPortIndex(const LibertyCell *cell, - const LibertyPort *port); + const LibertyPort *port); void findInputArcDelay(const Pin *drvr_pin, - Vertex *drvr_vertex, - const TimingArc *arc, - float from_slew, + Vertex *drvr_vertex, + const TimingArc *arc, + float from_slew, const Scene *scene, const MinMax *min_max, ArcDelayCalc *arc_delay_calc); void findDriverDelays(Vertex *drvr_vertex, - ArcDelayCalc *arc_delay_calc, + ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map); MultiDrvrNet *multiDrvrNet(const Vertex *drvr_vertex) const; MultiDrvrNet *findMultiDrvrNet(Vertex *drvr_vertex); @@ -198,8 +198,8 @@ protected: bool hasMultiDrvrs(Vertex *drvr_vertex); Vertex *firstLoad(Vertex *drvr_vertex); void findDriverDelays1(Vertex *drvr_vertex, - MultiDrvrNet *multi_drvr, - ArcDelayCalc *arc_delay_calc, + MultiDrvrNet *multi_drvr, + ArcDelayCalc *arc_delay_calc, LoadPinIndexMap &load_pin_index_map, RiseFallExists &load_slew_set); void initLoadSlews(Vertex *drvr_vertex); @@ -238,7 +238,7 @@ protected: void zeroUnsetLoadSlews(LoadPinIndexMap &load_pin_index_map, RiseFallExists &load_slew_set); void findVertexDelay(Vertex *vertex, - ArcDelayCalc *arc_delay_calc); + ArcDelayCalc *arc_delay_calc); DrvrLoadSlews loadSlews(LoadPinIndexMap &load_pin_index_map); void enqueueCheckEdges(Vertex *vertex); bool loadSlewChanged(Vertex *load_vertex, @@ -276,10 +276,10 @@ protected: DcalcAPIndex ap_index); void findLatchEdgeDelays(Edge *edge); void findCheckEdgeDelays(Edge *edge, - ArcDelayCalc *arc_delay_calc); + ArcDelayCalc *arc_delay_calc); void deleteMultiDrvrNets(); Slew checkEdgeClkSlew(const Vertex *from_vertex, - const RiseFall *from_rf, + const RiseFall *from_rf, const Scene *scene, const MinMax *min_max); float loadCap(const Pin *drvr_pin, @@ -356,11 +356,11 @@ public: void netCaps(const RiseFall *rf, const Scene *scene, const MinMax *min_max, - // Return values. - float &pin_cap, - float &wire_cap, - float &fanout, - bool &has_net_load) const; + // Return values. + float &pin_cap, + float &wire_cap, + float &fanout, + bool &has_net_load) const; void findCaps(const StaState *sta); private: From 577609daec7ab1e2952c3f804a687deb66176e3e Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sat, 10 Oct 2026 13:36:55 -0700 Subject: [PATCH 37/52] sta::max_fanout_violation_count preamble resolves #529 Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/Sta.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/search/Sta.cc b/search/Sta.cc index 1051cb05..5e21daff 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -5828,6 +5828,7 @@ size_t Sta::fanoutViolationCount(const MinMax *min_max, const ModeSeq &modes) { + checkFanoutPreamble(); FanoutCheckSeq &checks = check_fanouts_->check(nullptr, 0, true, modes, min_max); return checks.size(); } From 859dbba39b6d45ed03c0403031e952115d5876fd Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sat, 10 Oct 2026 15:11:48 -0700 Subject: [PATCH 38/52] set_max_borrow incremental resolves #527 Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- include/sta/Search.hh | 1 + search/Search.cc | 13 +++++++++++++ search/Sta.cc | 5 ++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 5661a387..27f13d19 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -120,6 +120,7 @@ public: // Invalidate vertex arrival time. void arrivalInvalid(Vertex *vertex); void arrivalInvalid(const Pin *pin); + void arrivalInvalid(const Instance *inst); // Invalidate all required times. void requiredsInvalid(); // Invalidate vertex required time. diff --git a/search/Search.cc b/search/Search.cc index 35270944..5b59f991 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -898,6 +898,19 @@ Search::arrivalInvalid(const Pin *pin) } } +void +Search::arrivalInvalid(const Instance *inst) +{ + if (graph_) { + InstancePinIterator *pin_iter = network_->pinIterator(inst); + while (pin_iter->hasNext()) { + Pin *pin = pin_iter->next(); + arrivalInvalid(pin); + } + delete pin_iter; + } +} + void Search::requiredInvalid(const Instance *inst) { diff --git a/search/Sta.cc b/search/Sta.cc index 5e21daff..934123d1 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -994,7 +994,9 @@ Sta::setLatchBorrowLimit(const Pin *pin, Sdc *sdc) { sdc->setLatchBorrowLimit(pin, limit); - search_->requiredInvalid(pin); + const Instance *inst = network_->instance(pin); + search_->arrivalInvalid(inst); + search_->requiredInvalid(inst); } void @@ -1003,6 +1005,7 @@ Sta::setLatchBorrowLimit(const Instance *inst, Sdc *sdc) { sdc->setLatchBorrowLimit(inst, limit); + search_->arrivalInvalid(inst); search_->requiredInvalid(inst); } From f861c6278db5c68198701da9c08a328830751829 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Sat, 10 Oct 2026 15:55:50 -0700 Subject: [PATCH 39/52] required init resolves #523 Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/Path.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/search/Path.cc b/search/Path.cc index 2dcc621e..45d93556 100644 --- a/search/Path.cc +++ b/search/Path.cc @@ -75,7 +75,7 @@ Path::Path(Vertex *vertex, const StaState *sta) : prev_path_(nullptr), arrival_(0.0), - required_(0.0), + required_(delayInitValue(tag->minMax()->opposite())), tag_index_(tag->index()), is_enum_(false), prev_arc_idx_(0) @@ -93,7 +93,7 @@ Path::Path(Vertex *vertex, const StaState *sta) : prev_path_(prev_path), arrival_(arrival), - required_(0.0), + required_(delayInitValue(tag->minMax()->opposite())), tag_index_(tag->index()), is_enum_(false) { @@ -118,7 +118,7 @@ Path::Path(Vertex *vertex, const StaState *sta) : prev_path_(prev_path), arrival_(arrival), - required_(0.0), + required_(delayInitValue(tag->minMax()->opposite())), tag_index_(tag->index()), is_enum_(is_enum) { @@ -157,7 +157,7 @@ Path::init(Vertex *vertex, tag_index_ = tag->index(), prev_path_ = nullptr; prev_arc_idx_ = 0; arrival_ = 0.0; - required_ = 0.0; + required_ = delayInitValue(tag->minMax()->opposite()); is_enum_ = false; } @@ -172,7 +172,7 @@ Path::init(Vertex *vertex, tag_index_ = tag->index(), prev_path_ = nullptr; prev_arc_idx_ = 0; arrival_ = arrival; - required_ = 0.0; + required_ = delayInitValue(tag->minMax()->opposite()); is_enum_ = false; } @@ -196,7 +196,7 @@ Path::init(Vertex *vertex, prev_arc_idx_ = 0; } arrival_ = arrival; - required_ = 0.0; + required_ = delayInitValue(tag->minMax()->opposite()); is_enum_ = false; } From 25e089d4c72825ce40736b9ac55d7dec6ad9faeb Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:56:28 -0700 Subject: [PATCH 40/52] Search::arrivalInvalid Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/Search.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/search/Search.cc b/search/Search.cc index 5b59f991..1cef0183 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -892,7 +892,8 @@ Search::arrivalInvalid(const Pin *pin) if (graph_) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - arrivalInvalid(vertex); + if (vertex) + arrivalInvalid(vertex); if (bidirect_drvr_vertex) arrivalInvalid(bidirect_drvr_vertex); } From 6f37a1d892a933b25b70580bee0620bfdc418c74 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Mon, 12 Oct 2026 13:24:44 -0700 Subject: [PATCH 41/52] Search::requiredInvalid Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- search/Search.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/search/Search.cc b/search/Search.cc index 1cef0183..83aa3e46 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -931,7 +931,8 @@ Search::requiredInvalid(const Pin *pin) if (graph_) { Vertex *vertex, *bidirect_drvr_vertex; graph_->pinVertices(pin, vertex, bidirect_drvr_vertex); - requiredInvalid(vertex); + if (vertex) + requiredInvalid(vertex); if (bidirect_drvr_vertex) requiredInvalid(bidirect_drvr_vertex); } From 62be933ffa265a0bd1b64ada1e180f10a0b6f67e Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Mon, 12 Oct 2026 21:37:34 +0000 Subject: [PATCH 42/52] test: use const Net* for highestNetAbove in network gtest Upstream ed4450b8 ("Network::highestConnectedNet const") changed highestNetAbove/highestConnectedNet to take and return const Net*. Signed-off-by: dsengupta0628 --- network/test/cpp/TestNetwork.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/test/cpp/TestNetwork.cc b/network/test/cpp/TestNetwork.cc index 881b85b7..6704d060 100644 --- a/network/test/cpp/TestNetwork.cc +++ b/network/test/cpp/TestNetwork.cc @@ -1810,7 +1810,7 @@ TEST_F(ConcreteNetworkLinkedTest, IsConnectedNetNet) { // Network: highestNetAbove TEST_F(ConcreteNetworkLinkedTest, HighestNetAbove) { - Net *highest = network_.highestNetAbove(net1_); + const Net *highest = network_.highestNetAbove(net1_); EXPECT_EQ(highest, net1_); } From af5c611a95fb0f48f0f3078f666bf31820d08731 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Mon, 12 Oct 2026 21:39:09 +0000 Subject: [PATCH 43/52] test: update search goldens for required init change Upstream f861c627 ("required init resolves #523") initializes path required times to INF instead of 0.0, so paths whose required time is never computed now report required/slack as INF (1e+30). In search_network_sta_deep, reg1/CK is no longer queued for required recalculation since its required time no longer changes from 0 to INF. Signed-off-by: dsengupta0628 --- search/test/search_fanin_fanout_deep.ok | 2 +- search/test/search_multiclock.ok | 18 +++++++++--------- search/test/search_network_sta_deep.ok | 2 -- search/test/search_property.ok | 4 ++-- search/test/search_property_deep.ok | 4 ++-- search/test/search_report_path_detail.ok | 4 ++-- search/test/search_report_path_expanded.ok | 20 ++++++++++---------- 7 files changed, 26 insertions(+), 28 deletions(-) diff --git a/search/test/search_fanin_fanout_deep.ok b/search/test/search_fanin_fanout_deep.ok index 91861f82..0752876f 100644 --- a/search/test/search_fanin_fanout_deep.ok +++ b/search/test/search_fanin_fanout_deep.ok @@ -216,7 +216,7 @@ Found 10 paths endpoint: reg1/D slack: 8.807621298956292e-9 arrival: 1.153597661840422e-9 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 16 start_pin: in3 --- worst_slack_vertex --- diff --git a/search/test/search_multiclock.ok b/search/test/search_multiclock.ok index 2ce9c57a..a490980c 100644 --- a/search/test/search_multiclock.ok +++ b/search/test/search_multiclock.ok @@ -870,31 +870,31 @@ Path Type: max --- PathEnd attributes on different types --- vertex pin: reg1/D path arrival: 1.0481947532170466e-9 - path required: 0.0 + path required: 1.0000000150474662e+30 vertex pin: reg1/D path arrival: 1.0459734189893766e-9 - path required: 0.0 + path required: 1.0000000150474662e+30 vertex pin: reg1/D path arrival: 1.0453631293927401e-9 - path required: 0.0 + path required: 1.0000000150474662e+30 vertex pin: out1 path arrival: 8.148756669434931e-11 - path required: 0.0 + path required: 1.0000000150474662e+30 vertex pin: out1 path arrival: 7.541753332951373e-11 - path required: 0.0 + path required: 1.0000000150474662e+30 vertex pin: reg2/D path arrival: 1.0092450486443028e-10 - path required: 0.0 + path required: 1.0000000150474662e+30 vertex pin: reg2/D path arrival: 1.0341291711846168e-10 - path required: 0.0 + path required: 1.0000000150474662e+30 vertex pin: reg1/D path arrival: 1.0459734189893766e-9 - path required: 0.0 + path required: 1.0000000150474662e+30 vertex pin: reg1/D path arrival: 1.0440722730820085e-9 - path required: 0.0 + path required: 1.0000000150474662e+30 --- find_timing_paths min_max --- Warning 502: search_multiclock.tcl line 1, find_timing_paths -endpoint_count is deprecated. Use -endpoint_path_count instead. Min_max paths: 18 diff --git a/search/test/search_network_sta_deep.ok b/search/test/search_network_sta_deep.ok index dc44e057..39ec489a 100644 --- a/search/test/search_network_sta_deep.ok +++ b/search/test/search_network_sta_deep.ok @@ -270,8 +270,6 @@ Vertex out1 Level 40 buf1/Z buf2/Z -Level 10 - reg1/CK --- find_requireds --- --- report_annotated_delay --- Not diff --git a/search/test/search_property.ok b/search/test/search_property.ok index 3ab43159..34f85202 100644 --- a/search/test/search_property.ok +++ b/search/test/search_property.ok @@ -80,8 +80,8 @@ pathend slack: 7.899714 --- Path properties --- path pin: out1 path arrival: 0.100286 -path required: 0.000000 -path slack: -0.100286 +path required: INF +path slack: INF --- Edge additional properties --- edge to_pin: and1/ZN --- get_property with -object_type --- diff --git a/search/test/search_property_deep.ok b/search/test/search_property_deep.ok index b348a36d..54d4e081 100644 --- a/search/test/search_property_deep.ok +++ b/search/test/search_property_deep.ok @@ -89,8 +89,8 @@ endpoint_clock: clk --- Path properties deep --- path pin: out1 path arrival: 1.0361974472905544e-10 -path required: 0.0 -path slack: -1.0361974472905544e-10 +path required: 1.0000000150474662e+30 +path slack: 1.0000000150474662e+30 path edge: ^ path pins count: 8 --- report_checks -format full --- diff --git a/search/test/search_report_path_detail.ok b/search/test/search_report_path_detail.ok index a2c378bb..a6a3a318 100644 --- a/search/test/search_report_path_detail.ok +++ b/search/test/search_report_path_detail.ok @@ -87,8 +87,8 @@ Warning 502: search_report_path_detail.tcl line 1, find_timing_paths -endpoint_c clk_skew: 0.0 --- Path methods --- arrival: 1.0028596009181712e-10 - required: 0.0 - slack: -1.0028596009181712e-10 + required: 1.0000000150474662e+30 + slack: 1.0000000150474662e+30 pin: out1 edge: ^ tag: 18 default ^max clk ^ clk_src clk crpr_pin null diff --git a/search/test/search_report_path_expanded.ok b/search/test/search_report_path_expanded.ok index 554b9342..ce450a84 100644 --- a/search/test/search_report_path_expanded.ok +++ b/search/test/search_report_path_expanded.ok @@ -1562,70 +1562,70 @@ Path 0: pin: out1 slack: 7.842025695481425e-9 arrival: 1.5797398111860872e-10 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 10 start_path pin: reg2/Q Path 1: pin: out1 slack: 7.844318083982671e-9 arrival: 1.5568145383948462e-10 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 10 start_path pin: reg2/Q Path 2: pin: reg1/D slack: 8.941200668743932e-9 arrival: 1.0481947532170466e-9 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 6 start_path pin: in2 Path 3: pin: reg1/D slack: 8.943422002971602e-9 arrival: 1.0459734189893766e-9 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 6 start_path pin: in1 Path 4: pin: reg1/D slack: 8.950316932043734e-9 arrival: 1.0453631293927401e-9 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 6 start_path pin: in2 Path 5: pin: reg1/D slack: 8.951607455287558e-9 arrival: 1.0440722730820085e-9 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 6 start_path pin: in1 Path 6: pin: reg2/D slack: 9.885211760263246e-9 arrival: 1.2980823360653204e-10 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 8 start_path pin: reg1/Q Path 7: pin: reg2/D slack: 9.889370211624282e-9 arrival: 1.3226435224833466e-10 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 8 start_path pin: reg1/Q Path 8: pin: out2 slack: 5.8740736719187225e-9 arrival: 1.2592588083393252e-10 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 8 start_path pin: reg3/Q Path 9: pin: out2 slack: 5.875777642216917e-9 arrival: 1.2422182726901099e-10 - required: 0.0 + required: 1.0000000150474662e+30 path_pins: 8 start_path pin: reg3/Q --- report_path_end with prev_end chaining --- From d90a451fe8b968359ae3554180c147951be6bed0 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Mon, 12 Oct 2026 21:40:42 +0000 Subject: [PATCH 44/52] test: find requireds before vertexWorst*Path gtests The StaDesignTest fixture only runs updateTiming(), which does not compute required times. Before upstream f861c627 uncomputed requireds were 0.0, so these tests found a path with a bogus finite slack; now they are INF and no path is selected. Call findRequireds() first. Signed-off-by: dsengupta0628 --- search/test/cpp/TestSearchStaDesign.cc | 3 +++ search/test/cpp/TestSearchStaDesignB.cc | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/search/test/cpp/TestSearchStaDesign.cc b/search/test/cpp/TestSearchStaDesign.cc index 06a2b39d..172ce5d8 100644 --- a/search/test/cpp/TestSearchStaDesign.cc +++ b/search/test/cpp/TestSearchStaDesign.cc @@ -288,6 +288,7 @@ TEST_F(StaDesignTest, VertexWorstRequiredPath) { TEST_F(StaDesignTest, VertexWorstRequiredPathRf) { Vertex *v = findVertex("r3/D"); ASSERT_NE(v, nullptr); + sta_->findRequireds(); Path *path = sta_->vertexWorstRequiredPath(v, RiseFall::rise(), MinMax::max()); EXPECT_NE(path, nullptr); } @@ -2205,6 +2206,7 @@ TEST_F(StaDesignTest, VertexWorstArrivalPathRf) { TEST_F(StaDesignTest, VertexWorstSlackPath) { Vertex *v = findVertex("r3/D"); ASSERT_NE(v, nullptr); + sta_->findRequireds(); Path *path = sta_->vertexWorstSlackPath(v, MinMax::max()); EXPECT_NE(path, nullptr); } @@ -2212,6 +2214,7 @@ TEST_F(StaDesignTest, VertexWorstSlackPath) { TEST_F(StaDesignTest, VertexWorstSlackPathRf) { Vertex *v = findVertex("r3/D"); ASSERT_NE(v, nullptr); + sta_->findRequireds(); Path *path = sta_->vertexWorstSlackPath(v, RiseFall::rise(), MinMax::max()); EXPECT_NE(path, nullptr); } diff --git a/search/test/cpp/TestSearchStaDesignB.cc b/search/test/cpp/TestSearchStaDesignB.cc index 556608fb..0047c983 100644 --- a/search/test/cpp/TestSearchStaDesignB.cc +++ b/search/test/cpp/TestSearchStaDesignB.cc @@ -479,6 +479,7 @@ TEST_F(StaDesignTest, StaWorstSlackCornerVertex) { TEST_F(StaDesignTest, StaVertexWorstSlackPath) { Vertex *v = findVertex("r3/D"); ASSERT_NE(v, nullptr); + sta_->findRequireds(); Path *path = sta_->vertexWorstSlackPath(v, MinMax::max()); EXPECT_NE(path, nullptr); } @@ -486,6 +487,7 @@ TEST_F(StaDesignTest, StaVertexWorstSlackPath) { TEST_F(StaDesignTest, StaVertexWorstSlackPathRf) { Vertex *v = findVertex("r3/D"); ASSERT_NE(v, nullptr); + sta_->findRequireds(); Path *path = sta_->vertexWorstSlackPath(v, RiseFall::rise(), MinMax::max()); EXPECT_NE(path, nullptr); } @@ -493,6 +495,7 @@ TEST_F(StaDesignTest, StaVertexWorstSlackPathRf) { TEST_F(StaDesignTest, StaVertexWorstRequiredPath) { Vertex *v = findVertex("r3/D"); ASSERT_NE(v, nullptr); + sta_->findRequireds(); Path *path = sta_->vertexWorstRequiredPath(v, MinMax::max()); EXPECT_NE(path, nullptr); } @@ -500,6 +503,7 @@ TEST_F(StaDesignTest, StaVertexWorstRequiredPath) { TEST_F(StaDesignTest, StaVertexWorstRequiredPathRf) { Vertex *v = findVertex("r3/D"); ASSERT_NE(v, nullptr); + sta_->findRequireds(); Path *path = sta_->vertexWorstRequiredPath(v, RiseFall::rise(), MinMax::max()); EXPECT_NE(path, nullptr); } From fcabb3422562a27142b8d094108e3d4ee336ae5e Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Mon, 12 Oct 2026 21:42:03 +0000 Subject: [PATCH 45/52] test: drop nullptr pin latch borrow limit gtest Since upstream 859dbba3 ("set_max_borrow incremental"), Sta::setLatchBorrowLimit(const Pin*) looks up the pin's instance, so a nullptr pin crashes. The StaInitTest fixture has no design to supply a real pin; the pin overload is covered on a linked design by SdcDesignTest (TestSdcStaDesign.cc) and tcl.search.latch_timing. Signed-off-by: dsengupta0628 --- search/test/cpp/TestSearchStaInit.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/search/test/cpp/TestSearchStaInit.cc b/search/test/cpp/TestSearchStaInit.cc index 69be7715..c6b7e8f9 100644 --- a/search/test/cpp/TestSearchStaInit.cc +++ b/search/test/cpp/TestSearchStaInit.cc @@ -3040,12 +3040,6 @@ TEST_F(StaInitTest, StaSetDriveResistanceNull) { } // --- Sta.cc: borrow limits --- -TEST_F(StaInitTest, StaSetLatchBorrowLimitPin) { - sta_->setLatchBorrowLimit(static_cast(nullptr), 1.0f, sta_->cmdSdc()); - // No crash - -} - TEST_F(StaInitTest, StaSetLatchBorrowLimitInst) { sta_->setLatchBorrowLimit(static_cast(nullptr), 1.0f, sta_->cmdSdc()); // No crash From fc6ed303107df32cd6240e6fe2a1c0cd4a2751ee Mon Sep 17 00:00:00 2001 From: Deepashree Sengupta Date: Mon, 12 Oct 2026 20:54:49 -0400 Subject: [PATCH 46/52] Graph skip wire edges from passive bidirect pads on driven nets (#525) Signed-off-by: dsengupta0628 --- graph/Graph.cc | 40 +++++++++++++++++++++++- include/sta/Graph.hh | 2 ++ test/bidirect_pad_fanout.lib | 25 +++++++++++++++ test/bidirect_pad_fanout.ok | 60 ++++++++++++++++++++++++++++++++++++ test/bidirect_pad_fanout.tcl | 16 ++++++++++ test/bidirect_pad_fanout.v | 28 +++++++++++++++++ test/regression_vars.tcl | 1 + 7 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 test/bidirect_pad_fanout.lib create mode 100644 test/bidirect_pad_fanout.ok create mode 100644 test/bidirect_pad_fanout.tcl create mode 100644 test/bidirect_pad_fanout.v diff --git a/graph/Graph.cc b/graph/Graph.cc index 77a2691d..da1710e3 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -262,6 +262,29 @@ Graph::makeInstDrvrWireEdges(const Instance *inst, delete pin_iter; } +// Bidirect pins with no fanin (pads/bumps with no timing arcs) are +// only loads if the net has another driver. Making wire edges from +// them is N*M for nets with many pads. +bool +Graph::isPassiveBidirectDrvr(const Pin *drvr_pin) const +{ + if (!network_->direction(drvr_pin)->isBidirect() + || network_->isTopLevelPort(drvr_pin)) + return false; + Vertex *drvr_vertex = pinDrvrVertex(drvr_pin); + return drvr_vertex && !drvr_vertex->hasFanin(); +} + +bool +Graph::hasActiveDrvr(const PinSeq &drvrs) const +{ + for (const Pin *drvr_pin : drvrs) { + if (!isPassiveBidirectDrvr(drvr_pin)) + return true; + } + return false; +} + void Graph::makeWireEdgesFromPin(const Pin *drvr_pin) { @@ -270,6 +293,9 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin) FindNetDrvrLoads visitor(drvr_pin, visited_drvrs, loads, drvrs, network_); network_->visitConnectedPins(drvr_pin, visitor); + if (isPassiveBidirectDrvr(drvr_pin) + && hasActiveDrvr(drvrs)) + return; for (auto load_pin : loads) { if (drvr_pin != load_pin) makeWireEdge(drvr_pin, load_pin); @@ -295,7 +321,11 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin, return; } + bool has_active_drvr = hasActiveDrvr(drvrs); for (auto drvr_pin : drvrs) { + if (has_active_drvr + && isPassiveBidirectDrvr(drvr_pin)) + continue; for (auto load_pin : loads) { if (drvr_pin != load_pin) makeWireEdge(drvr_pin, load_pin); @@ -337,8 +367,16 @@ Graph::makeWireEdgesToPin(const Pin *to_pin) { PinSet *drvrs = network_->drivers(to_pin); if (drvrs) { + bool has_active_drvr = false; + for (auto drvr : *drvrs) { + if (!isPassiveBidirectDrvr(drvr)) { + has_active_drvr = true; + break; + } + } for (auto drvr : *drvrs) { - if (drvr != to_pin) + if (drvr != to_pin + && !(has_active_drvr && isPassiveBidirectDrvr(drvr))) makeWireEdge(drvr, to_pin); } } diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 9229fe42..bd058ec8 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -205,6 +205,8 @@ protected: PinSet &visited_drvrs); bool isIsolatedNet(PinSeq &drvrs, PinSeq &loads) const; + bool isPassiveBidirectDrvr(const Pin *drvr_pin) const; + bool hasActiveDrvr(const PinSeq &drvrs) const; void makeWireEdges(); void makeInstDrvrWireEdges(const Instance *inst, PinSet &visited_drvrs); diff --git a/test/bidirect_pad_fanout.lib b/test/bidirect_pad_fanout.lib new file mode 100644 index 00000000..6402616b --- /dev/null +++ b/test/bidirect_pad_fanout.lib @@ -0,0 +1,25 @@ +library (bidirect_pad_fanout) { + delay_model : table_lookup ; + time_unit : "1ps" ; + voltage_unit : "1V" ; + current_unit : "1mA" ; + pulling_resistance_unit : "1kohm" ; + capacitive_load_unit (1, ff) ; + nom_process : 1.0 ; + nom_temperature : 25.0 ; + nom_voltage : 0.7 ; + cell (TIELO) { + area : 1.0 ; + pin (L) { + direction : output ; + function : "0" ; + } + } + cell (PAD_INOUT) { + area : 100.0 ; + pin (PAD) { + direction : inout ; + capacitance : 1.0 ; + } + } +} diff --git a/test/bidirect_pad_fanout.ok b/test/bidirect_pad_fanout.ok new file mode 100644 index 00000000..512ad440 --- /dev/null +++ b/test/bidirect_pad_fanout.ok @@ -0,0 +1,60 @@ +tie/L 6 +tie_pad0/PAD 0 +sig_drvr/Y 5 +sig_pad0/PAD 0 +clk_pad0/PAD 4 +Startpoint: in1 (input port clocked by clk) +Endpoint: sig_q0 (output port clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 v input external delay + 0.00 0.00 v in1 (in) + 21.98 21.98 v sig_drvr/Y (BUFx2_ASAP7_75t_R) + 20.16 42.15 v sig_load0/Y (BUFx2_ASAP7_75t_R) + 0.00 42.15 v sig_q0 (out) + 42.15 data arrival time + + 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 0.00 500.00 output external delay + 500.00 data required time +--------------------------------------------------------- + 500.00 data required time + -42.15 data arrival time +--------------------------------------------------------- + 457.85 slack (MET) + + +Startpoint: ff0 (rising edge-triggered flip-flop clocked by clk) +Endpoint: ff1 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ ff0/CLK (DFFHQx4_ASAP7_75t_R) + 64.80 64.80 ^ ff0/Q (DFFHQx4_ASAP7_75t_R) + 0.00 64.80 ^ ff1/D (DFFHQx4_ASAP7_75t_R) + 64.80 data arrival time + + 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ ff1/CLK (DFFHQx4_ASAP7_75t_R) + -12.94 487.06 library setup time + 487.06 data required time +--------------------------------------------------------- + 487.06 data required time + -64.80 data arrival time +--------------------------------------------------------- + 422.26 slack (MET) + + diff --git a/test/bidirect_pad_fanout.tcl b/test/bidirect_pad_fanout.tcl new file mode 100644 index 00000000..c51380ac --- /dev/null +++ b/test/bidirect_pad_fanout.tcl @@ -0,0 +1,16 @@ +# bidirect pads with no timing arcs on nets with a real driver +read_liberty asap7_small.lib.gz +read_liberty bidirect_pad_fanout.lib +read_verilog bidirect_pad_fanout.v +link_design top + +create_clock -name clk -period 500 [get_pins clk_pad0/PAD] +set_input_delay -clock clk 0 {in1 in2} +set_output_delay -clock clk 0 [all_outputs] + +foreach pin {tie/L tie_pad0/PAD sig_drvr/Y sig_pad0/PAD clk_pad0/PAD} { + puts "$pin [llength [get_timing_edges -from [get_pins $pin]]]" +} + +report_checks -from in1 -to sig_q0 +report_checks -from ff0/CLK -to ff1/D diff --git a/test/bidirect_pad_fanout.v b/test/bidirect_pad_fanout.v new file mode 100644 index 00000000..7b140d11 --- /dev/null +++ b/test/bidirect_pad_fanout.v @@ -0,0 +1,28 @@ +module top (in1, in2, tie_out, tie_q0, tie_q1, sig_q0, sig_q1, ff_q1); + input in1, in2; + output tie_out, tie_q0, tie_q1, sig_q0, sig_q1, ff_q1; + wire sig, clk_pad, ff_q0; + + // Constant net: tie cell + output port + passive pads + loads. + TIELO tie (.L(tie_out)); + PAD_INOUT tie_pad0 (.PAD(tie_out)); + PAD_INOUT tie_pad1 (.PAD(tie_out)); + PAD_INOUT tie_pad2 (.PAD(tie_out)); + BUFx2_ASAP7_75t_R tie_load0 (.A(tie_out), .Y(tie_q0)); + BUFx2_ASAP7_75t_R tie_load1 (.A(tie_out), .Y(tie_q1)); + + // Signal net: real driver + passive pads + loads. + BUFx2_ASAP7_75t_R sig_drvr (.A(in1), .Y(sig)); + PAD_INOUT sig_pad0 (.PAD(sig)); + PAD_INOUT sig_pad1 (.PAD(sig)); + PAD_INOUT sig_pad2 (.PAD(sig)); + BUFx2_ASAP7_75t_R sig_load0 (.A(sig), .Y(sig_q0)); + BUFx2_ASAP7_75t_R sig_load1 (.A(sig), .Y(sig_q1)); + + // Pad only net: clock defined on a pad. + PAD_INOUT clk_pad0 (.PAD(clk_pad)); + PAD_INOUT clk_pad1 (.PAD(clk_pad)); + PAD_INOUT clk_pad2 (.PAD(clk_pad)); + DFFHQx4_ASAP7_75t_R ff0 (.D(in2), .CLK(clk_pad), .Q(ff_q0)); + DFFHQx4_ASAP7_75t_R ff1 (.D(ff_q0), .CLK(clk_pad), .Q(ff_q1)); +endmodule diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index d505dab5..b107ab0b 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -141,6 +141,7 @@ record_example_tests { } record_public_tests { + bidirect_pad_fanout disable_clock_gating_check_cell disable_clock_gating_check_deprecated disable_clock_gating_check_inst From e7c4444a117b9b7dc15398583f2c4b302125b7b0 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:00:32 -0700 Subject: [PATCH 47/52] rm return/continue Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- graph/Graph.cc | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/graph/Graph.cc b/graph/Graph.cc index da1710e3..097155b8 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -293,12 +293,12 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin) FindNetDrvrLoads visitor(drvr_pin, visited_drvrs, loads, drvrs, network_); network_->visitConnectedPins(drvr_pin, visitor); - if (isPassiveBidirectDrvr(drvr_pin) - && hasActiveDrvr(drvrs)) - return; - for (auto load_pin : loads) { - if (drvr_pin != load_pin) - makeWireEdge(drvr_pin, load_pin); + if (!(isPassiveBidirectDrvr(drvr_pin) + && hasActiveDrvr(drvrs))) { + for (auto load_pin : loads) { + if (drvr_pin != load_pin) + makeWireEdge(drvr_pin, load_pin); + } } } @@ -318,17 +318,17 @@ Graph::makeWireEdgesFromPin(const Pin *drvr_pin, debugPrint(debug_, "graph", 1, "ignoring isolated driver {}", network_->pathName(drvr_pin)); } - return; } - - bool has_active_drvr = hasActiveDrvr(drvrs); - for (auto drvr_pin : drvrs) { - if (has_active_drvr - && isPassiveBidirectDrvr(drvr_pin)) - continue; - for (auto load_pin : loads) { - if (drvr_pin != load_pin) - makeWireEdge(drvr_pin, load_pin); + else { + bool has_active_drvr = hasActiveDrvr(drvrs); + for (auto drvr_pin : drvrs) { + if (!(has_active_drvr + && isPassiveBidirectDrvr(drvr_pin))) { + for (auto load_pin : loads) { + if (drvr_pin != load_pin) + makeWireEdge(drvr_pin, load_pin); + } + } } } } From 574500de9c633ab562195082d703ae587fcdcb54 Mon Sep 17 00:00:00 2001 From: James Cherry <34749589+jjcherry56@users.noreply.github.com> Date: Mon, 12 Oct 2026 17:10:42 -0700 Subject: [PATCH 48/52] endpoint predicate Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com> --- include/sta/Search.hh | 2 -- search/Search.cc | 37 +++++-------------------------------- search/Sta.cc | 2 +- 3 files changed, 6 insertions(+), 35 deletions(-) diff --git a/include/sta/Search.hh b/include/sta/Search.hh index 27f13d19..24f1aeef 100644 --- a/include/sta/Search.hh +++ b/include/sta/Search.hh @@ -203,7 +203,6 @@ public: // Find arrivals for the clock tree. void findClkArrivals(); EvalPred *evalPred() const { return eval_pred_; } - SearchPred *searchAdj() const { return search_thru_; } Tag *tag(TagIndex index) const; TagIndex tagCount() const; TagGroupIndex tagGroupCount() const; @@ -588,7 +587,6 @@ protected: bool crpr_approx_missing_requireds_{true}; // Search predicates. - SearchPred *search_thru_; SearchPred *search_adj_; EvalPred *eval_pred_; diff --git a/search/Search.cc b/search/Search.cc index 83aa3e46..57de6580 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -100,7 +100,8 @@ EvalPred::searchThru(Edge *edge, || !edge->isDisabledLoop()) && (search_thru_latches_ || role->isLatchDtoQ() - || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open); + || sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open) + && !sta_->isDisabledBidirectInstPath(edge); } bool @@ -117,31 +118,6 @@ EvalPred::searchTo(const Vertex *to_vertex, //////////////////////////////////////////////////////////////// -// EvalPred unless -// latch D->Q edge -class SearchThru : public EvalPred -{ -public: - SearchThru(const StaState *sta); - bool searchThru(Edge *edge, - const Mode *mode) const override; -}; - -SearchThru::SearchThru(const StaState *sta) : - EvalPred(sta) -{ -} - -bool -SearchThru::searchThru(Edge *edge, - const Mode *mode) const -{ - return EvalPred::searchThru(edge, mode) - && !edge->role()->isLatchDtoQ(); -} - -//////////////////////////////////////////////////////////////// - // SearchAdjLoop is mode independent. Search unless // latch D->Q edge // timing check edge @@ -243,7 +219,6 @@ SearchAdj::searchThru(Edge *edge) const Search::Search(StaState *sta) : StaState(sta), - search_thru_(new SearchThru(this)), search_adj_(new SearchAdj(this)), eval_pred_(new EvalPred(this)), @@ -298,7 +273,6 @@ Search::~Search() delete[] tags_; delete[] tag_groups_; delete tag_group_set_; - delete search_thru_; delete search_adj_; delete eval_pred_; delete arrival_visitor_; @@ -414,7 +388,6 @@ Search::copyState(const StaState *sta) required_iter_->copyState(sta); arrival_visitor_->copyState(sta); eval_pred_->copyState(sta); - search_thru_->copyState(sta); search_adj_->copyState(sta); visit_path_ends_->copyState(sta); gated_clk_->copyState(sta); @@ -3241,7 +3214,7 @@ bool Search::isEndpoint(Vertex *vertex) const { for (const Mode *mode : modes_) { - if (isEndpoint(vertex, search_thru_, mode)) + if (isEndpoint(vertex, eval_pred_, mode)) return true; } return false; @@ -3252,7 +3225,7 @@ Search::isEndpoint(Vertex *vertex, const ModeSeq &modes) const { for (const Mode *mode : modes) { - if (isEndpoint(vertex, search_thru_, mode)) + if (isEndpoint(vertex, eval_pred_, mode)) return true; } return false; @@ -3262,7 +3235,7 @@ bool Search::isEndpoint(Vertex *vertex, const Mode *mode) const { - return isEndpoint(vertex, search_thru_, mode); + return isEndpoint(vertex, eval_pred_, mode); } bool diff --git a/search/Sta.cc b/search/Sta.cc index 934123d1..507281a6 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -3607,7 +3607,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_, cmd_mode_)) + && !hasFanout(vertex, search_->evalPred(), graph_, cmd_mode_)) search_->seedRequired(vertex); else search_->findRequireds(vertex->level()); From 580a11f51f071230b7fa550275a07683d125bbe5 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Tue, 13 Oct 2026 15:11:14 +0000 Subject: [PATCH 49/52] test: drop searchAdj gtests Upstream 574500de ("endpoint predicate") removed Search::searchAdj() and the SearchThru predicate behind it. These tests only checked the accessor returned non-null; evalPred() keeps its own coverage. Signed-off-by: dsengupta0628 --- search/test/cpp/TestSearchStaDesign.cc | 8 +------- search/test/cpp/TestSearchStaInit.cc | 6 ------ search/test/cpp/TestSearchStaInitB.cc | 11 +---------- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/search/test/cpp/TestSearchStaDesign.cc b/search/test/cpp/TestSearchStaDesign.cc index 172ce5d8..77ca1a5b 100644 --- a/search/test/cpp/TestSearchStaDesign.cc +++ b/search/test/cpp/TestSearchStaDesign.cc @@ -2416,7 +2416,7 @@ TEST_F(StaDesignTest, OperatingConditions) { }() )); } -// --- Search: evalPred / searchAdj --- +// --- Search: evalPred --- TEST_F(StaDesignTest, SearchEvalPred) { Search *search = sta_->search(); @@ -2424,12 +2424,6 @@ TEST_F(StaDesignTest, SearchEvalPred) { EXPECT_NE(ep, nullptr); } -TEST_F(StaDesignTest, SearchSearchAdj) { - Search *search = sta_->search(); - SearchPred *sp = search->searchAdj(); - EXPECT_NE(sp, nullptr); -} - // --- Search: endpointInvalid --- TEST_F(StaDesignTest, SearchEndpointInvalid) { diff --git a/search/test/cpp/TestSearchStaInit.cc b/search/test/cpp/TestSearchStaInit.cc index c6b7e8f9..6bdade94 100644 --- a/search/test/cpp/TestSearchStaInit.cc +++ b/search/test/cpp/TestSearchStaInit.cc @@ -1489,12 +1489,6 @@ TEST_F(StaInitTest, SearchEvalPred) { EXPECT_NE(search->evalPred(), nullptr); } -TEST_F(StaInitTest, SearchSearchAdj) { - Search *search = sta_->search(); - ASSERT_NE(search, nullptr); - EXPECT_NE(search->searchAdj(), nullptr); -} - TEST_F(StaInitTest, SearchClear) { Search *search = sta_->search(); search->clear(); diff --git a/search/test/cpp/TestSearchStaInitB.cc b/search/test/cpp/TestSearchStaInitB.cc index 63fdfe22..f82f2a1a 100644 --- a/search/test/cpp/TestSearchStaInitB.cc +++ b/search/test/cpp/TestSearchStaInitB.cc @@ -2353,7 +2353,7 @@ TEST_F(StaInitTest, SearchRequiredIterator) { }() )); } -// === Search: evalPred and searchAdj === +// === Search: evalPred === TEST_F(StaInitTest, SearchEvalPred2) { ASSERT_NO_THROW(( [&](){ @@ -2364,15 +2364,6 @@ TEST_F(StaInitTest, SearchEvalPred2) { }() )); } -TEST_F(StaInitTest, SearchSearchAdj2) { - ASSERT_NO_THROW(( [&](){ - Search *search = sta_->search(); - SearchPred *adj = search->searchAdj(); - EXPECT_NE(adj, nullptr); - expectStaCoreState(sta_); - }() )); -} - // === Sta: isClock(Net*) === // === Sta: pins(Clock*) === From 1ebf8116db5a02d6491b3952a54525643cc737d7 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Tue, 13 Oct 2026 15:13:45 +0000 Subject: [PATCH 50/52] test: add regression tests for 10/2026 upstream fixes Tcl tests for issues found while merging upstream OpenSTA and fixed upstream. They use only existing test/ libraries (asap7_small, gf180mcu_sram) and reg1_asap7.v, plus a small hierarchical netlist and SPEF for spef_hier_reduce. - disable_timing_load_slew: load slews left at +/-INF on disabled drivers (9ee64aea; fixed 9d588bc9, 99ffdffd) - parasitic_network_new_net: makeParasiticNetwork on a net created after parasitics were pre-built (47f91f28; fixed 6d8f13f2) - wireload_before_link: set_wire_load_model with no linked design (47f91f28; fixed 6d8f13f2) - spef_hier_reduce: read_spef -path -reduce on a hierarchical port net (becb3294; fixed 7b261909) - set_max_time_borrow_pg_pin: set_max_time_borrow pin/cell forms on a cell with power/ground pins (859dbba3; fixed 25e089d4, 6f37a1d8) Signed-off-by: dsengupta0628 --- test/disable_timing_load_slew.ok | 60 ++++++++++++++++++++++++++++ test/disable_timing_load_slew.tcl | 18 +++++++++ test/parasitic_network_new_net.ok | 60 ++++++++++++++++++++++++++++ test/parasitic_network_new_net.tcl | 19 +++++++++ test/set_max_time_borrow_pg_pin.ok | 61 +++++++++++++++++++++++++++++ test/set_max_time_borrow_pg_pin.tcl | 16 ++++++++ test/spef_hier_reduce.ok | 18 +++++++++ test/spef_hier_reduce.spef | 29 ++++++++++++++ test/spef_hier_reduce.tcl | 10 +++++ test/spef_hier_reduce.v | 13 ++++++ test/wireload_before_link.ok | 1 + test/wireload_before_link.tcl | 6 +++ 12 files changed, 311 insertions(+) create mode 100644 test/disable_timing_load_slew.ok create mode 100644 test/disable_timing_load_slew.tcl create mode 100644 test/parasitic_network_new_net.ok create mode 100644 test/parasitic_network_new_net.tcl create mode 100644 test/set_max_time_borrow_pg_pin.ok create mode 100644 test/set_max_time_borrow_pg_pin.tcl create mode 100644 test/spef_hier_reduce.ok create mode 100644 test/spef_hier_reduce.spef create mode 100644 test/spef_hier_reduce.tcl create mode 100644 test/spef_hier_reduce.v create mode 100644 test/wireload_before_link.ok create mode 100644 test/wireload_before_link.tcl diff --git a/test/disable_timing_load_slew.ok b/test/disable_timing_load_slew.ok new file mode 100644 index 00000000..9ed36056 --- /dev/null +++ b/test/disable_timing_load_slew.ok @@ -0,0 +1,60 @@ +--- before set_disable_timing +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Slew Delay Time Description +------------------------------------------------------------------- + 0.000 0.000 0.000 clock clk (rise edge) + 0.000 0.000 clock network delay (ideal) + 0.000 0.000 0.000 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 8.957 64.765 64.765 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 6.921 16.469 81.234 ^ u1/Y (BUFx2_ASAP7_75t_R) + 8.119 17.814 99.048 ^ u2/Y (AND2x2_ASAP7_75t_R) + 8.119 0.000 99.048 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 99.048 data arrival time + + 0.000 500.000 500.000 clock clk (rise edge) + 0.000 500.000 clock network delay (ideal) + 0.000 500.000 clock reconvergence pessimism + 500.000 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -12.787 487.213 library setup time + 487.213 data required time +------------------------------------------------------------------- + 487.213 data required time + -99.048 data arrival time +------------------------------------------------------------------- + 388.166 slack (MET) + + +--- after set_disable_timing [get_cells u1] +Startpoint: r1 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Slew Delay Time Description +------------------------------------------------------------------- + 0.000 0.000 0.000 clock clk (rise edge) + 0.000 0.000 clock network delay (ideal) + 0.000 0.000 0.000 ^ r1/CLK (DFFHQx4_ASAP7_75t_R) + 8.861 64.686 64.686 ^ r1/Q (DFFHQx4_ASAP7_75t_R) + 8.135 18.357 83.043 ^ u2/Y (AND2x2_ASAP7_75t_R) + 8.135 0.000 83.043 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 83.043 data arrival time + + 0.000 500.000 500.000 clock clk (rise edge) + 0.000 500.000 clock network delay (ideal) + 0.000 500.000 clock reconvergence pessimism + 500.000 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -12.789 487.211 library setup time + 487.211 data required time +------------------------------------------------------------------- + 487.211 data required time + -83.043 data arrival time +------------------------------------------------------------------- + 404.168 slack (MET) + + +u2/B ^ 0.00:0.00 v 0.00:0.00 diff --git a/test/disable_timing_load_slew.tcl b/test/disable_timing_load_slew.tcl new file mode 100644 index 00000000..99314582 --- /dev/null +++ b/test/disable_timing_load_slew.tcl @@ -0,0 +1,18 @@ +# Disabled driver arcs must not leave load slews at the min/max init +# value (+/-INF). Regression for upstream 9ee64aea, fixed in 9d588bc9/99ffdffd. +read_liberty asap7_small.lib.gz +read_verilog reg1_asap7.v +link_design top +create_clock -name clk -period 500 {clk1 clk2 clk3} +set_input_delay -clock clk 1 {in1 in2} + +puts "--- before set_disable_timing" +report_checks -to r3/D -fields {slew} -digits 3 + +set_disable_timing [get_cells u1] + +puts "--- after set_disable_timing \[get_cells u1\]" +report_checks -to r3/D -fields {slew} -digits 3 + +# u2/B is the load of the disabled driver u1/Y. +report_slews [get_pins u2/B] diff --git a/test/parasitic_network_new_net.ok b/test/parasitic_network_new_net.ok new file mode 100644 index 00000000..6ccbe041 --- /dev/null +++ b/test/parasitic_network_new_net.ok @@ -0,0 +1,60 @@ +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 157.62 157.62 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 227.27 384.89 ^ u1/Y (BUFx2_ASAP7_75t_R) + 241.93 626.82 ^ u2/Y (AND2x2_ASAP7_75t_R) + 0.00 626.82 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 626.82 data arrival time + + 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -38.56 461.44 library setup time + 461.44 data required time +--------------------------------------------------------- + 461.44 data required time + -626.82 data arrival time +--------------------------------------------------------- + -165.38 slack (VIOLATED) + + +--- insert buffer u3 on new net n_new +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 157.62 157.62 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 227.27 384.89 ^ u1/Y (BUFx2_ASAP7_75t_R) + 58.03 442.92 ^ u2/Y (AND2x2_ASAP7_75t_R) + 199.85 642.76 ^ u3/Y (BUFx2_ASAP7_75t_R) + 0.00 642.76 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 642.76 data arrival time + + 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -38.52 461.48 library setup time + 461.48 data required time +--------------------------------------------------------- + 461.48 data required time + -642.76 data arrival time +--------------------------------------------------------- + -181.29 slack (VIOLATED) + + diff --git a/test/parasitic_network_new_net.tcl b/test/parasitic_network_new_net.tcl new file mode 100644 index 00000000..5b5f7e5d --- /dev/null +++ b/test/parasitic_network_new_net.tcl @@ -0,0 +1,19 @@ +# makeParasiticNetwork on a net created after parasitics were pre-built +# (set_wire_load_model) must not crash. Regression for upstream 47f91f28, +# fixed in 6d8f13f2. +read_liberty asap7_small.lib.gz +read_liberty gf180mcu_sram.lib.gz +read_verilog reg1_asap7.v +link_design top +create_clock -name clk -period 500 {clk1 clk2 clk3} +set_delay_calculator arnoldi +set_wire_load_model -name Estimate -library [get_name [lindex [get_libs gf180*] 0]] +report_checks -to r3/D +puts "--- insert buffer u3 on new net n_new" +make_instance u3 asap7_small/BUFx2_ASAP7_75t_R +disconnect_pin u2z r3/D +make_net n_new +connect_pin u2z u3/A +connect_pin n_new u3/Y +connect_pin n_new r3/D +report_checks -to r3/D diff --git a/test/set_max_time_borrow_pg_pin.ok b/test/set_max_time_borrow_pg_pin.ok new file mode 100644 index 00000000..b065db5a --- /dev/null +++ b/test/set_max_time_borrow_pg_pin.ok @@ -0,0 +1,61 @@ +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 64.76 64.76 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 16.47 81.23 ^ u1/Y (BUFx2_ASAP7_75t_R) + 17.81 99.05 ^ u2/Y (AND2x2_ASAP7_75t_R) + 0.00 99.05 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 99.05 data arrival time + + 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -12.79 487.21 library setup time + 487.21 data required time +--------------------------------------------------------- + 487.21 data required time + -99.05 data arrival time +--------------------------------------------------------- + 388.17 slack (MET) + + +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CLK (DFFHQx4_ASAP7_75t_R) + 64.76 64.76 ^ r2/Q (DFFHQx4_ASAP7_75t_R) + 16.47 81.23 ^ u1/Y (BUFx2_ASAP7_75t_R) + 17.81 99.05 ^ u2/Y (AND2x2_ASAP7_75t_R) + 0.00 99.05 ^ r3/D (DFFHQx4_ASAP7_75t_R) + 99.05 data arrival time + + 500.00 500.00 clock clk (rise edge) + 0.00 500.00 clock network delay (ideal) + 0.00 500.00 clock reconvergence pessimism + 500.00 ^ r3/CLK (DFFHQx4_ASAP7_75t_R) + -12.79 487.21 library setup time + 487.21 data required time +--------------------------------------------------------- + 487.21 data required time + -99.05 data arrival time +--------------------------------------------------------- + 388.17 slack (MET) + + +tns max 0.00 +tns max 0.00 +no crash diff --git a/test/set_max_time_borrow_pg_pin.tcl b/test/set_max_time_borrow_pg_pin.tcl new file mode 100644 index 00000000..d30d3ee7 --- /dev/null +++ b/test/set_max_time_borrow_pg_pin.tcl @@ -0,0 +1,16 @@ +# set_max_time_borrow on a pin or cell after timing has run must skip the +# cell's power/ground pins, which have no graph vertex. Regression for +# upstream 859dbba3 (arrivalInvalid) and the older requiredInvalid gap, +# fixed in 25e089d4/6f37a1d8. +read_liberty asap7_small.lib.gz +read_verilog reg1_asap7.v +link_design top +create_clock -name clk -period 500 {clk1 clk2 clk3} +report_checks +set_max_time_borrow 1 [get_pins r1/CLK] +report_checks +# Cell form with required times already computed. +report_tns +set_max_time_borrow 1 [get_cells r1] +report_tns +puts "no crash" diff --git a/test/spef_hier_reduce.ok b/test/spef_hier_reduce.ok new file mode 100644 index 00000000..f7c3b305 --- /dev/null +++ b/test/spef_hier_reduce.ok @@ -0,0 +1,18 @@ +Net n1 + Pin capacitance: 0.39-0.53 + Wire capacitance: 0.60-0.47 + Total capacitance: 0.99-1.00 + Number of drivers: 1 + Number of loads: 1 + Number of pins: 2 + +Driver pins + sub1/u1/Y output (BUFx2_ASAP7_75t_R) + +Load pins + u2/A input (BUFx2_ASAP7_75t_R) 0.39-0.53 + +Hierarchical pins + sub1/out output + +no crash diff --git a/test/spef_hier_reduce.spef b/test/spef_hier_reduce.spef new file mode 100644 index 00000000..0acc41cd --- /dev/null +++ b/test/spef_hier_reduce.spef @@ -0,0 +1,29 @@ +*SPEF "IEEE 1481-1998" +*DESIGN "sub" +*DATE "Thu Oct 9 2026" +*VENDOR "None" +*PROGRAM "None" +*VERSION "1.0" +*DESIGN_FLOW "NETLIST_TYPE_VERILOG" +*DIVIDER / +*DELIMITER : +*BUS_DELIMITER [ ] +*T_UNIT 1 PS +*C_UNIT 1 FF +*R_UNIT 1 KOHM +*L_UNIT 1 UH + +*PORTS +in I +out O + +*D_NET out 1.0 +*CONN +*I u1:Y O +*P out O +*CAP +1 u1:Y 0.5 +2 out 0.5 +*RES +1 u1:Y out 0.1 +*END diff --git a/test/spef_hier_reduce.tcl b/test/spef_hier_reduce.tcl new file mode 100644 index 00000000..f0e6766e --- /dev/null +++ b/test/spef_hier_reduce.tcl @@ -0,0 +1,10 @@ +# read_spef -path -reduce on a net connected to a hierarchical port: the +# network is made for the highest connected net (n1) and must be reduced and +# deleted on that net, not the local net (sub1/out). Regression for upstream +# becb3294, fixed in 7b261909. +read_liberty asap7_small.lib.gz +read_verilog spef_hier_reduce.v +link_design top +read_spef -path sub1 -reduce spef_hier_reduce.spef +report_net n1 +puts "no crash" diff --git a/test/spef_hier_reduce.v b/test/spef_hier_reduce.v new file mode 100644 index 00000000..7bbfb8c5 --- /dev/null +++ b/test/spef_hier_reduce.v @@ -0,0 +1,13 @@ +module sub (in, out); + input in; + output out; + BUFx2_ASAP7_75t_R u1 (.A(in), .Y(out)); +endmodule + +module top (a, z); + input a; + output z; + wire n1; + sub sub1 (.in(a), .out(n1)); + BUFx2_ASAP7_75t_R u2 (.A(n1), .Y(z)); +endmodule diff --git a/test/wireload_before_link.ok b/test/wireload_before_link.ok new file mode 100644 index 00000000..1a42f2bc --- /dev/null +++ b/test/wireload_before_link.ok @@ -0,0 +1 @@ +no crash diff --git a/test/wireload_before_link.tcl b/test/wireload_before_link.tcl new file mode 100644 index 00000000..706b0ece --- /dev/null +++ b/test/wireload_before_link.tcl @@ -0,0 +1,6 @@ +# set_wire_load_model before link_design (no top instance) must not crash. +# Regression for upstream 47f91f28, fixed in 6d8f13f2. +read_liberty asap7_small.lib.gz +read_liberty gf180mcu_sram.lib.gz +set_wire_load_model -name Estimate -library [get_name [lindex [get_libs gf180*] 0]] +puts "no crash" From d619d08e73952a5bb076f9cd91c4449a4a291cf8 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Tue, 13 Oct 2026 15:34:33 +0000 Subject: [PATCH 51/52] test: register new regression tests in regression_vars.tcl Signed-off-by: dsengupta0628 --- test/regression_vars.tcl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index b107ab0b..557244ab 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -148,6 +148,7 @@ record_public_tests { disable_clock_gating_check_pin disable_clock_gating_check_port disable_clock_gating_check_sdc + disable_timing_load_slew disconnect_mcp_pin dmp_two_pole_reduce equiv_cells_ff_latch @@ -171,6 +172,7 @@ record_public_tests { max_power_area non_seq_timing package_require + parasitic_network_new_net path_group_names port_delay_delete_inst power_network_changed @@ -182,6 +184,7 @@ record_public_tests { report_checks_src_attr report_json1 report_json2 + set_max_time_borrow_pg_pin set_path_margin1 set_path_margin2 set_path_margin3 @@ -189,6 +192,7 @@ record_public_tests { set_path_margin5 set_path_margin6 slash_port_test + spef_hier_reduce suppress_msg swig_seq_double_free swig_seq_leak @@ -206,6 +210,7 @@ record_public_tests { verilog_write_escape verilog_write_gzip verilog_unconnected_hpin + wireload_before_link write_path_spice_arc_sense } From 80fbbb2945fee2702a84e711a95a0a3bb7050f22 Mon Sep 17 00:00:00 2001 From: dsengupta0628 Date: Tue, 13 Oct 2026 15:37:30 +0000 Subject: [PATCH 52/52] test: fail disable_timing_load_slew on INF load slews Signed-off-by: dsengupta0628 --- test/disable_timing_load_slew.tcl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/disable_timing_load_slew.tcl b/test/disable_timing_load_slew.tcl index 99314582..4788f65d 100644 --- a/test/disable_timing_load_slew.tcl +++ b/test/disable_timing_load_slew.tcl @@ -16,3 +16,10 @@ report_checks -to r3/D -fields {slew} -digits 3 # u2/B is the load of the disabled driver u1/Y. report_slews [get_pins u2/B] +# Fail (non-zero exit) if the load slews were left at +/-INF. +foreach prop {slew_max slew_min} { + set slew [get_property [get_pins u2/B] $prop] + if { abs($slew) > 1e10 } { + error "u2/B $prop is $slew" + } +}