Levelize rewrite

commit 34f392b19f13b8a55371f0149de8fec3e76aeffa
Author: James Cherry <cherry@parallaxsw.com>
Date:   Thu Apr 17 13:21:45 2025 -0700

    in_degree itr

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 745e11326d109dddf798843cbbefc4aac79a8bc8
Author: James Cherry <cherry@parallaxsw.com>
Date:   Thu Apr 17 11:00:52 2025 -0700

    levelize invalid cleanup

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 501cfc7ebbc601db7f87c4c1fe70224fca904ce7
Author: James Cherry <cherry@parallaxsw.com>
Date:   Thu Apr 17 10:15:16 2025 -0700

    refactor

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit b1f0245f9ad7c7c140580e46a0e9c14742b6b448
Author: James Cherry <cherry@parallaxsw.com>
Date:   Thu Apr 17 09:47:05 2025 -0700

    Levelize use bit field for on_path

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 1ecb617bf0894a290d6240a6150f1d31653e56fb
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Apr 16 19:17:56 2025 -0700

    levelize static alloc

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 100c7c5a10581f52f6b5c7c2d4b5614d19549e23
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Apr 16 18:15:39 2025 -0700

    levelize use visited flag

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit a72bdc4322c3ea9f34d0144fdf1f74acfbed39fb
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Apr 16 15:59:25 2025 -0700

    findCycleBackEdges

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 21f5e9737cfd7c41a5444592920320cfe0de559c
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Apr 16 15:51:17 2025 -0700

    Levels find back edges from roots with fanout

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit ca2fe71e506c61f05401a52f3b6df3153c2e2393
Author: James Cherry <cherry@parallaxsw.com>
Date:   Tue Apr 15 19:26:50 2025 -0700

    rm Vertex::color

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 628a973c69eab99929d32a2f5b1810dce71cbab1
Author: James Cherry <cherry@parallaxsw.com>
Date:   Tue Apr 15 18:45:19 2025 -0700

    set max_level in Levelize::setLevel

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 874c8e10691a47fff06a89c66d66dbb63def9682
Author: James Cherry <cherry@parallaxsw.com>
Date:   Tue Apr 15 17:29:22 2025 -0700

    levelize passes all regressions

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 790eae362a7423aa7e18432faaae7459a56976d7
Author: James Cherry <cherry@parallaxsw.com>
Date:   Tue Apr 15 08:45:27 2025 -0700

    levelize stats

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 26067a99a4fbc81d94dbc1022ea5d9ddd64780a0
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 22:20:56 2025 -0700

    pass fast

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 635bf478117014dd22d27c4f6bd65b260e51ec35
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 18:00:03 2025 -0700

    comment

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 5fd2e9d1dd3a00494d56d967d21251f20e3a1f2c
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 17:59:00 2025 -0700

    rm levelizeFrom()

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 078205723e2ce1f9be2e1360be80e5f9d25e978b
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 14:46:48 2025 -0700

    leak

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 412c9a8ead792ee01e46a545c065c174e8342abc
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 13:41:07 2025 -0700

    levelize level init

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 7883e3b1bac990f33cf7fce24fe43a0614f43daa
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 12:59:45 2025 -0700

    relevelize insert only after levelized

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 263da8dc56469f0408d1cfbc7c40a079bc90dc9f
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 11:58:55 2025 -0700

    topo level init

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 9de2ae3128a9ae427653c13152aa796f76277419
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 11:21:14 2025 -0700

    levelize pass fast

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 8ba8310850ed1cdaf718b93bf44966043ab2042d
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Apr 14 10:04:42 2025 -0700

    constant latch en

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 8f543c3d5b261373d9b8f2da000322e529da7fb4
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Apr 13 19:36:23 2025 -0700

    levelize topological sort

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 4c2ae84839edd9f6797b834d7fd4f644e77855f8
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Apr 13 16:14:36 2025 -0700

    levelize bfs cleanup

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 186f50e8255fcae814f98cc7f253cfd3ba9a6c1d
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Apr 13 15:25:00 2025 -0700

    levelize bfs

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 1cefb185caaea64fc568c5d1ba4e7f5a961b0ba3
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Apr 13 12:16:49 2025 -0700

    levelize with bfs

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 5181e6b13ff38b52d13460e6abfee3202d4935c0
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sat Apr 12 18:33:44 2025 -0700

    findBackEdges use sorted roots

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit b25ac1a7d0ec2e835cddbf53930fa5f9f2a5b299
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sat Apr 12 15:32:33 2025 -0700

    find back edges

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2025-04-17 16:53:55 -07:00
parent 1329b9c55a
commit 400c473fe3
10 changed files with 485 additions and 223 deletions

View File

@ -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

View File

@ -44,8 +44,6 @@ namespace sta {
class MinMax;
class Sdc;
enum class LevelColor { white, gray, black };
typedef ObjectTable<Vertex> VertexTable;
typedef ObjectTable<Edge> EdgeTable;
typedef Map<const Pin*, Vertex*> 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<LevelColor>(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<uint8_t> 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;

View File

@ -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;

View File

@ -4086,7 +4086,7 @@ Sdc::makeFilterPath(ExceptionFrom *from,
void
Sdc::makeLoopExceptions()
{
for (GraphLoop *loop : *levelize_->loops())
for (GraphLoop *loop : levelize_->loops())
makeLoopExceptions(loop);
}

View File

@ -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++;
}

View File

@ -25,6 +25,7 @@
#include "Levelize.hh"
#include <algorithm>
#include <deque>
#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<Vertex*, int> 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<Vertex*> 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 &degree_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");
}
////////////////////////////////////////////////////////////////

View File

@ -24,9 +24,12 @@
#pragma once
#include <stack>
#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<Vertex*,VertexOutEdgeIterator*> VertexEdgeIterPair;
typedef std::stack<VertexEdgeIterPair> 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_;

View File

@ -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();

View File

@ -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("");
}

View File

@ -3529,7 +3529,7 @@ Sta::vertexLevel(Vertex *vertex)
return vertex->level();
}
GraphLoopSeq *
GraphLoopSeq &
Sta::graphLoops()
{
ensureLevelized();