From 400c473fe384773a4788ee8378238462b4291fe3 Mon Sep 17 00:00:00 2001 From: James Cherry Date: Thu, 17 Apr 2025 16:53:55 -0700 Subject: [PATCH] Levelize rewrite commit 34f392b19f13b8a55371f0149de8fec3e76aeffa Author: James Cherry Date: Thu Apr 17 13:21:45 2025 -0700 in_degree itr Signed-off-by: James Cherry commit 745e11326d109dddf798843cbbefc4aac79a8bc8 Author: James Cherry Date: Thu Apr 17 11:00:52 2025 -0700 levelize invalid cleanup Signed-off-by: James Cherry commit 501cfc7ebbc601db7f87c4c1fe70224fca904ce7 Author: James Cherry Date: Thu Apr 17 10:15:16 2025 -0700 refactor Signed-off-by: James Cherry commit b1f0245f9ad7c7c140580e46a0e9c14742b6b448 Author: James Cherry Date: Thu Apr 17 09:47:05 2025 -0700 Levelize use bit field for on_path Signed-off-by: James Cherry commit 1ecb617bf0894a290d6240a6150f1d31653e56fb Author: James Cherry Date: Wed Apr 16 19:17:56 2025 -0700 levelize static alloc Signed-off-by: James Cherry commit 100c7c5a10581f52f6b5c7c2d4b5614d19549e23 Author: James Cherry Date: Wed Apr 16 18:15:39 2025 -0700 levelize use visited flag Signed-off-by: James Cherry commit a72bdc4322c3ea9f34d0144fdf1f74acfbed39fb Author: James Cherry Date: Wed Apr 16 15:59:25 2025 -0700 findCycleBackEdges Signed-off-by: James Cherry commit 21f5e9737cfd7c41a5444592920320cfe0de559c Author: James Cherry Date: Wed Apr 16 15:51:17 2025 -0700 Levels find back edges from roots with fanout Signed-off-by: James Cherry commit ca2fe71e506c61f05401a52f3b6df3153c2e2393 Author: James Cherry Date: Tue Apr 15 19:26:50 2025 -0700 rm Vertex::color Signed-off-by: James Cherry commit 628a973c69eab99929d32a2f5b1810dce71cbab1 Author: James Cherry Date: Tue Apr 15 18:45:19 2025 -0700 set max_level in Levelize::setLevel Signed-off-by: James Cherry commit 874c8e10691a47fff06a89c66d66dbb63def9682 Author: James Cherry Date: Tue Apr 15 17:29:22 2025 -0700 levelize passes all regressions Signed-off-by: James Cherry commit 790eae362a7423aa7e18432faaae7459a56976d7 Author: James Cherry Date: Tue Apr 15 08:45:27 2025 -0700 levelize stats Signed-off-by: James Cherry commit 26067a99a4fbc81d94dbc1022ea5d9ddd64780a0 Author: James Cherry Date: Mon Apr 14 22:20:56 2025 -0700 pass fast Signed-off-by: James Cherry commit 635bf478117014dd22d27c4f6bd65b260e51ec35 Author: James Cherry Date: Mon Apr 14 18:00:03 2025 -0700 comment Signed-off-by: James Cherry commit 5fd2e9d1dd3a00494d56d967d21251f20e3a1f2c Author: James Cherry Date: Mon Apr 14 17:59:00 2025 -0700 rm levelizeFrom() Signed-off-by: James Cherry commit 078205723e2ce1f9be2e1360be80e5f9d25e978b Author: James Cherry Date: Mon Apr 14 14:46:48 2025 -0700 leak Signed-off-by: James Cherry commit 412c9a8ead792ee01e46a545c065c174e8342abc Author: James Cherry Date: Mon Apr 14 13:41:07 2025 -0700 levelize level init Signed-off-by: James Cherry commit 7883e3b1bac990f33cf7fce24fe43a0614f43daa Author: James Cherry Date: Mon Apr 14 12:59:45 2025 -0700 relevelize insert only after levelized Signed-off-by: James Cherry commit 263da8dc56469f0408d1cfbc7c40a079bc90dc9f Author: James Cherry Date: Mon Apr 14 11:58:55 2025 -0700 topo level init Signed-off-by: James Cherry commit 9de2ae3128a9ae427653c13152aa796f76277419 Author: James Cherry Date: Mon Apr 14 11:21:14 2025 -0700 levelize pass fast Signed-off-by: James Cherry commit 8ba8310850ed1cdaf718b93bf44966043ab2042d Author: James Cherry Date: Mon Apr 14 10:04:42 2025 -0700 constant latch en Signed-off-by: James Cherry commit 8f543c3d5b261373d9b8f2da000322e529da7fb4 Author: James Cherry Date: Sun Apr 13 19:36:23 2025 -0700 levelize topological sort Signed-off-by: James Cherry commit 4c2ae84839edd9f6797b834d7fd4f644e77855f8 Author: James Cherry Date: Sun Apr 13 16:14:36 2025 -0700 levelize bfs cleanup Signed-off-by: James Cherry commit 186f50e8255fcae814f98cc7f253cfd3ba9a6c1d Author: James Cherry Date: Sun Apr 13 15:25:00 2025 -0700 levelize bfs Signed-off-by: James Cherry commit 1cefb185caaea64fc568c5d1ba4e7f5a961b0ba3 Author: James Cherry Date: Sun Apr 13 12:16:49 2025 -0700 levelize with bfs Signed-off-by: James Cherry commit 5181e6b13ff38b52d13460e6abfee3202d4935c0 Author: James Cherry Date: Sat Apr 12 18:33:44 2025 -0700 findBackEdges use sorted roots Signed-off-by: James Cherry commit b25ac1a7d0ec2e835cddbf53930fa5f9f2a5b299 Author: James Cherry Date: Sat Apr 12 15:32:33 2025 -0700 find back edges Signed-off-by: James Cherry Signed-off-by: James Cherry --- graph/Graph.cc | 13 +- include/sta/Graph.hh | 18 +- include/sta/Sta.hh | 2 +- sdc/Sdc.cc | 2 +- search/CheckTiming.cc | 6 +- search/Levelize.cc | 611 +++++++++++++++++++++++++++++------------- search/Levelize.hh | 46 +++- search/Search.cc | 6 +- search/Search.i | 2 +- search/Sta.cc | 2 +- 10 files changed, 485 insertions(+), 223 deletions(-) diff --git a/graph/Graph.cc b/graph/Graph.cc index aa78eedf..1902f485 100644 --- a/graph/Graph.cc +++ b/graph/Graph.cc @@ -969,8 +969,9 @@ Vertex::init(Pin *pin, is_check_clk_ = false; is_constrained_ = false; has_downstream_clk_pin_ = false; - color_ = unsigned(LevelColor::white); level_ = 0; + visited1_ = false; + visited2_ = false; bfs_in_queue_ = 0; crpr_path_pruning_disabled_ = false; } @@ -1040,9 +1041,15 @@ Vertex::setLevel(Level level) } void -Vertex::setColor(LevelColor color) +Vertex::setVisited(bool visited) { - color_ = unsigned(color); + visited1_ = visited; +} + +void +Vertex::setVisited2(bool visited) +{ + visited2_ = visited; } void diff --git a/include/sta/Graph.hh b/include/sta/Graph.hh index 365d979c..34e3e122 100644 --- a/include/sta/Graph.hh +++ b/include/sta/Graph.hh @@ -44,8 +44,6 @@ namespace sta { class MinMax; class Sdc; -enum class LevelColor { white, gray, black }; - typedef ObjectTable VertexTable; typedef ObjectTable EdgeTable; typedef Map PinVertexMap; @@ -254,11 +252,13 @@ public: bool isDriver(const Network *network) const; Level level() const { return level_; } void setLevel(Level level); + bool visited() const { return visited1_; } + void setVisited(bool visited); + bool visited2() const { return visited2_; } + void setVisited2(bool visited); bool isRoot() const{ return level_ == 0; } bool hasFanin() const; bool hasFanout() const; - LevelColor color() const { return static_cast(color_); } - void setColor(LevelColor color); Slew *slews() { return slews_; } const Slew *slews() const { return slews_; } Path *paths() const { return paths_; } @@ -330,16 +330,15 @@ protected: // Each bit corresponds to a different BFS queue. std::atomic bfs_in_queue_; // 8 - unsigned int level_:Graph::vertex_level_bits; // 24 + int level_:Graph::vertex_level_bits; // 24 unsigned int slew_annotated_:slew_annotated_bits; // 4 - // Levelization search state. - // LevelColor gcc barfs if this is dcl'd. - unsigned color_:2; // LogicValue gcc barfs if this is dcl'd. unsigned sim_value_:3; // Bidirect pins have two vertices. // This flag distinguishes the driver and load vertices. bool is_bidirect_drvr_:1; + + unsigned object_idx_:VertexTable::idx_bits; // 7 bool is_reg_clk_:1; bool is_disabled_constraint_:1; bool is_gated_clk_enable_:1; @@ -350,7 +349,8 @@ protected: bool is_constrained_:1; bool has_downstream_clk_pin_:1; bool crpr_path_pruning_disabled_:1; - unsigned object_idx_:VertexTable::idx_bits; // 7 + bool visited1_:1; + bool visited2_:1; private: friend class Graph; diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 37b396df..72a7d7d5 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -1048,7 +1048,7 @@ public: bool annotated); // Make sure levels are up to date and return vertex level. Level vertexLevel(Vertex *vertex); - GraphLoopSeq *graphLoops(); + GraphLoopSeq &graphLoops(); PathAnalysisPt *pathAnalysisPt(Path *path); DcalcAnalysisPt *pathDcalcAnalysisPt(Path *path); TagIndex tagCount() const; diff --git a/sdc/Sdc.cc b/sdc/Sdc.cc index aa02e319..0aa3c96d 100644 --- a/sdc/Sdc.cc +++ b/sdc/Sdc.cc @@ -4086,7 +4086,7 @@ Sdc::makeFilterPath(ExceptionFrom *from, void Sdc::makeLoopExceptions() { - for (GraphLoop *loop : *levelize_->loops()) + for (GraphLoop *loop : levelize_->loops()) makeLoopExceptions(loop); } diff --git a/search/CheckTiming.cc b/search/CheckTiming.cc index f6496356..84ca3feb 100644 --- a/search/CheckTiming.cc +++ b/search/CheckTiming.cc @@ -184,12 +184,10 @@ CheckTiming::checkLoops() { // These may not need to be sorted because the graph roots are // sorted during levelization so the discovery should be consistent. - GraphLoopSeq *loops = levelize_->loops(); + GraphLoopSeq &loops = levelize_->loops(); // Count the combinational loops. int loop_count = 0; - GraphLoopSeq::Iterator loop_iter1(loops); - while (loop_iter1.hasNext()) { - GraphLoop *loop = loop_iter1.next(); + for (GraphLoop *loop : loops) { if (loop->isCombinational()) loop_count++; } diff --git a/search/Levelize.cc b/search/Levelize.cc index b2f03e78..074ec759 100644 --- a/search/Levelize.cc +++ b/search/Levelize.cc @@ -25,6 +25,7 @@ #include "Levelize.hh" #include +#include #include "Report.hh" #include "Debug.hh" @@ -45,14 +46,13 @@ using std::max; Levelize::Levelize(StaState *sta) : StaState(sta), - search_pred_(new SearchPredNonLatch2(sta)), + search_pred_(sta), levelized_(false), levels_valid_(false), max_level_(0), level_space_(10), roots_(new VertexSet(graph_)), relevelize_from_(new VertexSet(graph_)), - loops_(nullptr), observer_(nullptr) { } @@ -61,9 +61,8 @@ Levelize::~Levelize() { delete roots_; delete relevelize_from_; - delete search_pred_; delete observer_; - deleteLoops(); + loops_.deleteContents(); } void @@ -87,7 +86,9 @@ Levelize::clear() roots_->clear(); relevelize_from_->clear(); clearLoopEdges(); - deleteLoops(); + loops_.deleteContentsClear(); + loop_edges_.clear(); + max_level_ = 0; } void @@ -101,17 +102,6 @@ Levelize::clearLoopEdges() disabled_loop_edges_.clear(); } -void -Levelize::deleteLoops() -{ - if (loops_) { - loops_->deleteContents(); - delete loops_; - loops_ = nullptr; - loop_edges_.clear(); - } -} - void Levelize::ensureLevelized() { @@ -123,29 +113,42 @@ Levelize::ensureLevelized() } } -// Depth first search. -// "Introduction to Algorithms", section 23.3 pg 478. +#define onPath() visied2() +#define setOnPath(on_path) setVisited2(on_path) + void Levelize::levelize() { Stats stats(debug_, report_); debugPrint(debug_, "levelize", 1, "levelize"); - max_level_ = 0; - clearLoopEdges(); - deleteLoops(); - loops_ = new GraphLoopSeq; + clear(); + + 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(); - VertexSeq roots; - // Sort the roots so that loop breaking is stable in regressions. - // In situations where port directions are broken pins may - // be treated as bidirects, leading to a plethora of degenerate - // roots that take forever to sort. - if (roots.size() < 100) - sortRoots(roots); - levelizeFrom(roots); + findBackEdges(); + VertexSeq topo_sorted = findToplologicalOrder(); + assignLevels(topo_sorted); ensureLatchLevels(); - // Find vertices in cycles that are were not accessible from roots. - levelizeCycles(); + + // Set level of stranded vertices (constants) to zero. + VertexIterator vertex_iter2(graph_); + while (vertex_iter2.hasNext()) { + Vertex *vertex = vertex_iter2.next(); + if (vertex->level() == -1) + setLevel(vertex, 0); + // cleanup + vertex->setVisited(false); + vertex->setOnPath(false); + } levelized_ = true; levels_valid_ = true; stats.report("Levelize"); @@ -154,128 +157,279 @@ Levelize::levelize() void Levelize::findRoots() { + roots_->clear(); VertexIterator vertex_iter(graph_); while (vertex_iter.hasNext()) { Vertex *vertex = vertex_iter.next(); - setLevel(vertex, 0); if (isRoot(vertex)) { - debugPrint(debug_, "levelize", 2, "root %s", - vertex->to_string(this).c_str()); + debugPrint(debug_, "levelize", 2, "root %s%s", + vertex->to_string(this).c_str(), + hasFanout(vertex) ? " fanout" : ""); roots_->insert(vertex); - if (hasFanout(vertex, search_pred_, graph_)) - // Color roots with no fanout black so that they are - // not treated as degenerate loops by levelizeCycles(). - vertex->setColor(LevelColor::black); } - else - vertex->setColor(LevelColor::white); + } + if (debug_->check("levelize", 1)) { + size_t fanout_roots = 0; + for (Vertex *root : *roots_) { + if (hasFanout(root)) + fanout_roots++; + } + debugPrint(debug_, "levelize", 1, "Found %zu roots %zu with fanout", + roots_->size(), + fanout_roots); } } -// Root vertices have at no enabled (non-disabled) edges entering them -// and are not disabled. +// Root vertices have at no non-disabled edges entering them +// and are not disabled and have non-disabled fanout edges. bool Levelize::isRoot(Vertex *vertex) { - if (search_pred_->searchTo(vertex)) { - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); + if (search_pred_.searchTo(vertex)) { + VertexInEdgeIterator edge_iter1(vertex, graph_); + while (edge_iter1.hasNext()) { + Edge *edge = edge_iter1.next(); Vertex *from_vertex = edge->from(graph_); - if (search_pred_->searchFrom(from_vertex) - && search_pred_->searchThru(edge)) - return false; + if (search_pred_.searchFrom(from_vertex) + && search_pred_.searchThru(edge)) + return false; } - // Bidirect pins are not treated as roots in this case. - return !graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()); + // Levelize bidirect driver as if it was a fanout of the bidirect load. + return !(graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) + && vertex->isBidirectDriver()); } else return false; } -void -Levelize::sortRoots(VertexSeq &roots) +bool +Levelize::hasFanout(Vertex *vertex) { - // roots_ is a set so insert/delete are fast for incremental changes. - // Copy the set into a vector for sorting. - for (Vertex *root : *roots_) - roots.push_back(root); - sort(roots, VertexNameLess(network_)); -} - -void -Levelize::levelizeFrom(VertexSeq &roots) -{ - VertexSeq::Iterator root_iter(roots); - while (root_iter.hasNext()) { - Vertex *root = root_iter.next(); - EdgeSeq path; - visit(root, 0, level_space_, path); - } -} - -void -Levelize::visit(Vertex *vertex, - Level level, - Level level_space, - EdgeSeq &path) -{ - Pin *from_pin = vertex->pin(); - debugPrint(debug_, "levelize", 3, "level %d %s", - level, vertex->to_string(this).c_str()); - vertex->setColor(LevelColor::gray); - setLevel(vertex, level); - max_level_ = max(level, max_level_); - level += level_space; - if (level >= Graph::vertex_level_max) - criticalError(616, "maximum logic level exceeded"); - - if (search_pred_->searchFrom(vertex)) { - VertexOutEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *to_vertex = edge->to(graph_); - if (search_pred_->searchThru(edge) - && search_pred_->searchTo(to_vertex)) { - LevelColor to_color = to_vertex->color(); - if (to_color == LevelColor::gray) - // Back edges form feedback loops. - recordLoop(edge, path); - else if (to_color == LevelColor::white - || to_vertex->level() < level) { - path.push_back(edge); - visit(to_vertex, level, level_space, path); - path.pop_back(); - } + bool has_fanout = false; + if (search_pred_.searchFrom(vertex)) { + VertexOutEdgeIterator edge_iter2(vertex, graph_); + while (edge_iter2.hasNext()) { + Edge *edge = edge_iter2.next(); + Vertex *to_vertex = edge->from(graph_); + if (search_pred_.searchTo(to_vertex) + && search_pred_.searchThru(edge)) { + has_fanout = true; + break; } - if (edge->role() == TimingRole::latchDtoQ()) - latch_d_to_q_edges_.insert(edge); } // Levelize bidirect driver as if it was a fanout of the bidirect load. - if (graph_delay_calc_->bidirectDrvrSlewFromLoad(from_pin) - && !vertex->isBidirectDriver()) { - Vertex *to_vertex = graph_->pinDrvrVertex(from_pin); - if (search_pred_->searchTo(to_vertex) - && (to_vertex->color() == LevelColor::white - || to_vertex->level() < level)) - visit(to_vertex, level, level_space, path); - } + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(vertex->pin()) + && !vertex->isBidirectDriver()) + has_fanout = true; } - vertex->setColor(LevelColor::black); + return has_fanout; } +// Non-recursive DFS to find back edges so the graph is acyclic. void -Levelize::reportPath(EdgeSeq &path) const +Levelize::findBackEdges() { - bool first_edge = true; - EdgeSeq::Iterator edge_iter(path); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - if (first_edge) - report_->reportLine(" %s", edge->from(graph_)->to_string(this).c_str()); - report_->reportLine(" %s", edge->to(graph_)->to_string(this).c_str()); - first_edge = false; + Stats stats(debug_, report_); + EdgeSeq path; + FindBackEdgesStack stack; + + VertexSeq sorted_roots = sortedRootsWithFanout(); + for (Vertex *vertex : sorted_roots) { + vertex->setVisited(true); + vertex->setOnPath(true); + stack.emplace(vertex, new VertexOutEdgeIterator(vertex, graph_)); } + + findBackEdges(path, stack); + findCycleBackEdges(); + stats.report("Levelize find back edges"); +} + +VertexSeq +Levelize::sortedRootsWithFanout() +{ + VertexSeq roots; + for (Vertex *root : *roots_) { + if (hasFanout(root)) + roots.push_back(root); + } + // Sort the roots so that loop breaking is stable in regressions. + // Skip sorting if it will take a long time. + if (roots.size() < 100) + sort(roots, VertexNameLess(network_)); + return roots; +} + +EdgeSet +Levelize::findBackEdges(EdgeSeq &path, + FindBackEdgesStack &stack) +{ + EdgeSet back_edges; + while (!stack.empty()) { + VertexEdgeIterPair vertex_iter = stack.top(); + Vertex *vertex = vertex_iter.first; + VertexOutEdgeIterator *edge_iter = vertex_iter.second; + if (edge_iter->hasNext()) { + Edge *edge = edge_iter->next(); + if (search_pred_.searchThru(edge)) { + Vertex *to_vertex = edge->to(graph_); + if (!to_vertex->visited()) { + to_vertex->setVisited(true); + to_vertex->setOnPath(true); + path.push_back(edge); + stack.emplace(to_vertex, new VertexOutEdgeIterator(to_vertex, graph_)); + } + else if (to_vertex->visited2()) { // on path + // Found a back edge (loop). + recordLoop(edge, path); + back_edges.insert(edge); + } + } + } + else { + delete edge_iter; + stack.pop(); + vertex->setOnPath(false); + if (!path.empty()) + path.pop_back(); + } + } + return back_edges; +} + +// Find back edges in cycles that are were not accessible from roots. +// Add roots for the disabled back edges so they are become accessible. +void +Levelize::findCycleBackEdges() +{ + // Search root-less cycles for back edges. + VertexSeq unvisited = findUnvisitedVertices(); + // Sort cycle vertices so results are stable. + // Skip sorting if it will take a long time. + if (unvisited.size() < 100) + sort(unvisited, VertexNameLess(network_)); + size_t back_edge_count = 0; + VertexSet visited(graph_); + for (Vertex *vertex : unvisited) { + if (visited.find(vertex) == visited.end()) { + VertexSet path_vertices(graph_); + EdgeSeq path; + FindBackEdgesStack stack; + visited.insert(vertex); + path_vertices.insert(vertex); + stack.emplace(vertex, new VertexOutEdgeIterator(vertex, graph_)); + EdgeSet back_edges = findBackEdges(path, stack); + for (Edge *back_edge : back_edges) + roots_->insert(back_edge->from(graph_)); + back_edge_count += back_edges.size(); + } + } + debugPrint(debug_, "levelize", 1, "Found %zu cycle back edges", back_edge_count); +} + +// Find vertices in cycles that are were not accessible from roots. +VertexSeq +Levelize::findUnvisitedVertices() +{ + VertexSeq unvisited; + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (!vertex->visited() + && search_pred_.searchFrom(vertex)) + unvisited.push_back(vertex); + } + return unvisited; +} + +//////////////////////////////////////////////////////////////// + +VertexSeq +Levelize::findToplologicalOrder() +{ + Stats stats(debug_, report_); + std::map in_degree; + + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (search_pred_.searchFrom(vertex)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (search_pred_.searchThru(edge) + && search_pred_.searchTo(to_vertex)) + in_degree[to_vertex] += 1; + if (edge->role() == TimingRole::latchDtoQ()) + latch_d_to_q_edges_.insert(edge); + } + // Levelize bidirect driver as if it was a fanout of the bidirect load. + const Pin *pin = vertex->pin(); + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) + && !vertex->isBidirectDriver()) { + Vertex *to_vertex = graph_->pinDrvrVertex(pin);; + if (search_pred_.searchTo(to_vertex)) + in_degree[to_vertex] += 1; + } + } + } + + std::deque queue; + for (Vertex *root : *roots_) + queue.push_back(root); + + VertexSeq topo_order; + while (!queue.empty()) { + Vertex *vertex = queue.front(); + queue.pop_front(); + topo_order.push_back(vertex); + if (search_pred_.searchFrom(vertex)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (search_pred_.searchThru(edge) + && search_pred_.searchTo(to_vertex)) { + const auto &to_degree_itr = in_degree.find(to_vertex); + int &to_in_degree = to_degree_itr->second; + to_in_degree -= 1; + if (to_in_degree == 0) + queue.push_back(to_vertex); + } + } + } + // Levelize bidirect driver as if it was a fanout of the bidirect load. + const Pin *pin = vertex->pin(); + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) + && !vertex->isBidirectDriver()) { + Vertex *to_vertex = graph_->pinDrvrVertex(pin); + if (search_pred_.searchTo(to_vertex)) { + const auto °ree_itr = in_degree.find(to_vertex); + int &in_degree = degree_itr->second; + in_degree -= 1; + if (in_degree == 0) + queue.push_back(to_vertex); + } + } + } + + if (debug_->check("levelize", 1)) { + VertexIterator vertex_iter(graph_); + while (vertex_iter.hasNext()) { + Vertex *vertex = vertex_iter.next(); + if (in_degree[vertex] != 0) + debugPrint(debug_, "levelize", 2, "topological sort missing %s", + vertex->to_string(this).c_str()); + } + } + if (debug_->check("levelize", 3)) { + report_->reportLine("Topological sort"); + for (Vertex *vertex : topo_order) + report_->reportLine("%s", vertex->to_string(this).c_str()); + } + stats.report("Levelize topological sort"); + return topo_order; } void @@ -285,14 +439,12 @@ Levelize::recordLoop(Edge *edge, debugPrint(debug_, "levelize", 2, "Loop edge %s (%s)", edge->to_string(this).c_str(), edge->role()->to_string().c_str()); - // Do not record loops if they have been invalidated. - if (loops_) { - EdgeSeq *loop_edges = loopEdges(path, edge); - GraphLoop *loop = new GraphLoop(loop_edges); - loops_->push_back(loop); - if (variables_->dynamicLoopBreaking()) - sdc_->makeLoopExceptions(loop); - } + EdgeSeq *loop_edges = loopEdges(path, edge); + GraphLoop *loop = new GraphLoop(loop_edges); + loops_.push_back(loop); + if (variables_->dynamicLoopBreaking()) + sdc_->makeLoopExceptions(loop); + // Record disabled loop edges so they can be cleared without // traversing the entire graph to find them. disabled_loop_edges_.insert(edge); @@ -328,6 +480,53 @@ Levelize::loopEdges(EdgeSeq &path, return loop_edges; } +void +Levelize::reportPath(EdgeSeq &path) const +{ + bool first_edge = true; + EdgeSeq::Iterator edge_iter(path); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + if (first_edge) + report_->reportLine(" %s", edge->from(graph_)->to_string(this).c_str()); + report_->reportLine(" %s", edge->to(graph_)->to_string(this).c_str()); + first_edge = false; + } +} + +//////////////////////////////////////////////////////////////// + +void +Levelize::assignLevels(VertexSeq &topo_sorted) +{ + for (Vertex *root : *roots_) + setLevel(root, 0); + for (Vertex *vertex : topo_sorted) { + if (vertex->level() != -1) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (search_pred_.searchThru(edge) + && search_pred_.searchTo(to_vertex)) + setLevel(to_vertex, max(to_vertex->level(), + vertex->level() + level_space_)); + } + // Levelize bidirect driver as if it was a fanout of the bidirect load. + const Pin *pin = vertex->pin(); + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(pin) + && !vertex->isBidirectDriver()) { + Vertex *to_vertex = graph_->pinDrvrVertex(pin); + if (search_pred_.searchTo(to_vertex)) + setLevel(to_vertex, max(to_vertex->level(), + vertex->level() + level_space_)); + } + } + } +} + +//////////////////////////////////////////////////////////////// + // Make sure latch D input level is not the same as the Q level. // This is because the Q arrival depends on the D arrival and // to find them in parallel they have to be scheduled separately @@ -346,84 +545,63 @@ Levelize::ensureLatchLevels() latch_d_to_q_edges_.clear(); } -void -Levelize::levelizeCycles() -{ - // Find vertices that were not discovered by searching from all - // graph roots. - VertexSeq uncolored; - VertexIterator vertex_iter(graph_); - while (vertex_iter.hasNext()) { - Vertex *vertex = vertex_iter.next(); - if (vertex->color() == LevelColor::white - && search_pred_->searchFrom(vertex)) - uncolored.push_back(vertex); - } - - // Sort cycle vertices so results are stable. - sort(uncolored, VertexNameLess(network_)); - - VertexSeq::Iterator uncolored_iter(uncolored); - while (uncolored_iter.hasNext()) { - Vertex *vertex = uncolored_iter.next(); - // Only search from and assign root status to vertices that - // previous searches did not visit. Otherwise "everybody is a - // root". - if (vertex->color() == LevelColor::white) { - EdgeSeq path; - roots_->insert(vertex); - visit(vertex, 0, level_space_, path); - } - } -} - void Levelize::invalid() { - debugPrint(debug_, "levelize", 1, "levels invalid"); - clear(); + if (levelized_) { + debugPrint(debug_, "levelize", 1, "levels invalid"); + levelized_ = false; + levels_valid_ = false; + } } void Levelize::invalidFrom(Vertex *vertex) { - debugPrint(debug_, "levelize", 1, "level invalid from %s", - vertex->to_string(this).c_str()); - VertexInEdgeIterator edge_iter(vertex, graph_); - while (edge_iter.hasNext()) { - Edge *edge = edge_iter.next(); - Vertex *from_vertex = edge->from(graph_); - relevelize_from_->insert(from_vertex); + if (levelized_) { + debugPrint(debug_, "levelize", 1, "level invalid from %s", + vertex->to_string(this).c_str()); + VertexInEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *from_vertex = edge->from(graph_); + relevelize_from_->insert(from_vertex); + } + relevelize_from_->insert(vertex); + levels_valid_ = false; } - relevelize_from_->insert(vertex); - levels_valid_ = false; } void Levelize::deleteVertexBefore(Vertex *vertex) { - roots_->erase(vertex); - relevelize_from_->erase(vertex); + if (levelized_) { + roots_->erase(vertex); + relevelize_from_->erase(vertex); + } } void Levelize::relevelizeFrom(Vertex *vertex) { - debugPrint(debug_, "levelize", 1, "invalid relevelize from %s", - vertex->to_string(this).c_str()); - relevelize_from_->insert(vertex); - levels_valid_ = false; + if (levelized_) { + debugPrint(debug_, "levelize", 1, "invalid relevelize from %s", + vertex->to_string(this).c_str()); + relevelize_from_->insert(vertex); + levels_valid_ = false; + } } void Levelize::deleteEdgeBefore(Edge *edge) { - if (loop_edges_.hasKey(edge)) { - // Relevelize if a loop edge is removed. Incremental levelization - // fails because the DFS path will be missing. - invalid(); - // Prevent refererence to deleted edge by clearLoopEdges(). + if (levelized_ + && loop_edges_.hasKey(edge)) { disabled_loop_edges_.erase(edge); + // Relevelize if a loop edge is removed. Incremental levelization + // fails because the DFS path will be missing. + levelized_ = false; + levels_valid_ = false; } } @@ -441,13 +619,15 @@ Levelize::relevelize() for (Vertex *vertex : *relevelize_from_) { debugPrint(debug_, "levelize", 1, "relevelize from %s", vertex->to_string(this).c_str()); - if (search_pred_->searchFrom(vertex)) { + if (search_pred_.searchFrom(vertex)) { if (isRoot(vertex)) { setLevel(vertex, 0); roots_->insert(vertex); } + VertexSet visited(graph_); + VertexSet path_vertices(graph_); EdgeSeq path; - visit(vertex, vertex->level(), 1, path); + visit(vertex, nullptr, vertex->level(), 1, visited, path_vertices, path); } } ensureLatchLevels(); @@ -455,6 +635,55 @@ Levelize::relevelize() relevelize_from_->clear(); } +void +Levelize::visit(Vertex *vertex, + Edge *from, + Level level, + Level level_space, + VertexSet &visited, + VertexSet &path_vertices, + EdgeSeq &path) +{ + Pin *from_pin = vertex->pin(); + setLevel(vertex, level); + level += level_space; + visited.insert(vertex); + path_vertices.insert(vertex); + if (from) + path.push_back(from); + + if (search_pred_.searchFrom(vertex)) { + VertexOutEdgeIterator edge_iter(vertex, graph_); + while (edge_iter.hasNext()) { + Edge *edge = edge_iter.next(); + Vertex *to_vertex = edge->to(graph_); + if (search_pred_.searchThru(edge) + && search_pred_.searchTo(to_vertex)) { + if (path_vertices.find(to_vertex) != path_vertices.end()) + // Back edges form feedback loops. + recordLoop(edge, path); + else if (visited.find(to_vertex) == visited.end() + && to_vertex->level() < level) + visit(to_vertex, edge, level, level_space, visited, path_vertices, path); + } + if (edge->role() == TimingRole::latchDtoQ()) + latch_d_to_q_edges_.insert(edge); + } + // Levelize bidirect driver as if it was a fanout of the bidirect load. + if (graph_delay_calc_->bidirectDrvrSlewFromLoad(from_pin) + && !vertex->isBidirectDriver()) { + Vertex *to_vertex = graph_->pinDrvrVertex(from_pin); + if (search_pred_.searchTo(to_vertex) + && (visited.find(to_vertex) == visited.end() + || to_vertex->level() < level)) + visit(to_vertex, nullptr, level, level_space, visited, path_vertices, path); + } + } + path_vertices.erase(vertex); + if (from) + path.pop_back(); +} + bool Levelize::isDisabledLoop(Edge *edge) const { @@ -465,11 +694,17 @@ void Levelize::setLevel(Vertex *vertex, Level level) { + debugPrint(debug_, "levelize", 2, "set level %s %d", + vertex->to_string(this).c_str(), + level); if (vertex->level() != level) { if (observer_) observer_->levelChangedBefore(vertex); vertex->setLevel(level); } + max_level_ = max(level, max_level_); + if (level >= Graph::vertex_level_max) + criticalError(616, "maximum logic level exceeded"); } //////////////////////////////////////////////////////////////// diff --git a/search/Levelize.hh b/search/Levelize.hh index 8cc3118f..7a5f8408 100644 --- a/search/Levelize.hh +++ b/search/Levelize.hh @@ -24,9 +24,12 @@ #pragma once +#include + #include "NetworkClass.hh" #include "SdcClass.hh" #include "GraphClass.hh" +#include "SearchPred.hh" #include "StaState.hh" namespace sta { @@ -34,10 +37,13 @@ namespace sta { class SearchPred; class LevelizeObserver; +typedef std::pair VertexEdgeIterPair; +typedef std::stack FindBackEdgesStack; + class Levelize : public StaState { public: - explicit Levelize(StaState *sta); + Levelize(StaState *sta); virtual ~Levelize(); // Space between initially assigned levels that is filled in by // incremental levelization. Set level space before levelization. @@ -54,40 +60,54 @@ public: // Vertices with no fanin edges. VertexSet *roots() { return roots_; } bool isRoot(Vertex *vertex); + bool hasFanout(Vertex *vertex); // Reset to virgin state. void clear(); // Edge is disabled to break combinational loops. bool isDisabledLoop(Edge *edge) const; // Only valid when levels are valid. - GraphLoopSeq *loops() { return loops_; } + GraphLoopSeq &loops() { return loops_; } // Set the observer for level changes. void setObserver(LevelizeObserver *observer); protected: void levelize(); void findRoots(); - void sortRoots(VertexSeq &roots); - void levelizeFrom(VertexSeq &roots); - void visit(Vertex *vertex, Level level, Level level_space, EdgeSeq &path); - void levelizeCycles(); - void relevelize(); - void clearLoopEdges(); - void deleteLoops(); - void recordLoop(Edge *edge, EdgeSeq &path); - EdgeSeq *loopEdges(EdgeSeq &path, Edge *closing_edge); + VertexSeq sortedRootsWithFanout(); + VertexSeq findToplologicalOrder(); + void assignLevels(VertexSeq &topo_sorted); + void recordLoop(Edge *edge, + EdgeSeq &path); + EdgeSeq *loopEdges(EdgeSeq &path, + Edge *closing_edge); void ensureLatchLevels(); + void findBackEdges(); + EdgeSet findBackEdges(EdgeSeq &path, + FindBackEdgesStack &stack); + void findCycleBackEdges(); + VertexSeq findUnvisitedVertices(); + void relevelize(); + void visit(Vertex *vertex, + Edge *from, + Level level, + Level level_space, + VertexSet &visited, + VertexSet &path_vertices, + EdgeSeq &path); void setLevel(Vertex *vertex, Level level); + void clearLoopEdges(); + void deleteLoops(); void reportPath(EdgeSeq &path) const; - SearchPred *search_pred_; + SearchPredNonLatch2 search_pred_; bool levelized_; bool levels_valid_; Level max_level_; Level level_space_; VertexSet *roots_; VertexSet *relevelize_from_; - GraphLoopSeq *loops_; + GraphLoopSeq loops_; EdgeSet loop_edges_; EdgeSet disabled_loop_edges_; EdgeSet latch_d_to_q_edges_; diff --git a/search/Search.cc b/search/Search.cc index 33239653..c143dd3c 100644 --- a/search/Search.cc +++ b/search/Search.cc @@ -3565,8 +3565,10 @@ Search::ensureDownstreamClkPins() // as having downstream clk pins. ClkTreeSearchPred pred(this); BfsBkwdIterator iter(BfsIndex::other, &pred, this); - for (Vertex *vertex : *graph_->regClkVertices()) - iter.enqueue(vertex); + for (Vertex *vertex : *graph_->regClkVertices()) { + if (!vertex->isConstant()) + iter.enqueue(vertex); + } while (iter.hasNext()) { Vertex *vertex = iter.next(); diff --git a/search/Search.i b/search/Search.i index 25f8c633..1ed6da1c 100644 --- a/search/Search.i +++ b/search/Search.i @@ -314,7 +314,7 @@ report_loops() { Sta *sta = Sta::sta(); Report *report = sta->report(); - for (GraphLoop *loop : *sta->graphLoops()) { + for (GraphLoop *loop : sta->graphLoops()) { loop->report(sta); report->reportLineString(""); } diff --git a/search/Sta.cc b/search/Sta.cc index 4bb2d137..0ef56510 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -3529,7 +3529,7 @@ Sta::vertexLevel(Vertex *vertex) return vertex->level(); } -GraphLoopSeq * +GraphLoopSeq & Sta::graphLoops() { ensureLevelized();