2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2025-01-22 02:54:33 +01:00
|
|
|
// Copyright (c) 2025, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2022-01-04 18:17:08 +01:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2018-09-28 17:54:21 +02:00
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
2022-01-04 18:17:08 +01:00
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2025-01-22 02:54:33 +01:00
|
|
|
//
|
|
|
|
|
// The origin of this software must not be misrepresented; you must not
|
|
|
|
|
// claim that you wrote the original software.
|
|
|
|
|
//
|
|
|
|
|
// Altered source versions must be plainly marked as such, and must not be
|
|
|
|
|
// misrepresented as being the original software.
|
|
|
|
|
//
|
|
|
|
|
// This notice may not be removed or altered from any source distribution.
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "PathEnum.hh"
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "Debug.hh"
|
|
|
|
|
#include "Error.hh"
|
|
|
|
|
#include "Fuzzy.hh"
|
|
|
|
|
#include "TimingRole.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "Sdc.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "PathAnalysisPt.hh"
|
|
|
|
|
#include "Tag.hh"
|
|
|
|
|
#include "Search.hh"
|
|
|
|
|
#include "PathEnd.hh"
|
2025-03-27 02:21:03 +01:00
|
|
|
#include "Path.hh"
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
// A diversion is an alternate path formed by changing the previous
|
|
|
|
|
// path/arc of before_div to after_div/div_arc in path.
|
|
|
|
|
//
|
|
|
|
|
// div_arc
|
|
|
|
|
// after_div<--------+
|
|
|
|
|
// |
|
|
|
|
|
// <--...--before_div<--...--path<---path_end
|
|
|
|
|
class Diversion
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Diversion(PathEnd *path_end,
|
|
|
|
|
Path *after_div);
|
|
|
|
|
PathEnd *pathEnd() const { return path_end_; }
|
|
|
|
|
Path *divPath() const { return after_div_; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
PathEnd *path_end_;
|
|
|
|
|
Path *after_div_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Diversion::Diversion(PathEnd *path_end,
|
|
|
|
|
Path *after_div) :
|
|
|
|
|
path_end_(path_end),
|
|
|
|
|
after_div_(after_div)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Default constructor required for DiversionQueue template.
|
|
|
|
|
DiversionGreater::DiversionGreater() :
|
2019-03-13 01:25:53 +01:00
|
|
|
sta_(nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
DiversionGreater::DiversionGreater(const StaState *sta) :
|
2018-09-28 17:54:21 +02:00
|
|
|
sta_(sta)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// It is important to break all ties in this comparison so that no two
|
|
|
|
|
// diversions are equal. Otherwise only one of a set of paths with
|
|
|
|
|
// the same delay is kept in the queue.
|
|
|
|
|
bool
|
|
|
|
|
DiversionGreater::operator()(Diversion *div1,
|
|
|
|
|
Diversion *div2) const
|
|
|
|
|
{
|
|
|
|
|
PathEnd *path_end1 = div1->pathEnd();
|
|
|
|
|
PathEnd *path_end2 = div2->pathEnd();
|
|
|
|
|
return PathEnd::cmp(path_end1, path_end2, sta_) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
deleteDiversionPathEnd(Diversion *div)
|
|
|
|
|
{
|
|
|
|
|
delete div->pathEnd();
|
|
|
|
|
delete div;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2025-03-27 02:21:03 +01:00
|
|
|
PathEnum::PathEnum(size_t group_path_count,
|
|
|
|
|
size_t endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
bool cmp_slack,
|
|
|
|
|
const StaState *sta) :
|
|
|
|
|
StaState(sta),
|
|
|
|
|
cmp_slack_(cmp_slack),
|
2024-11-24 00:38:26 +01:00
|
|
|
group_path_count_(group_path_count),
|
|
|
|
|
endpoint_path_count_(endpoint_path_count),
|
2018-09-28 17:54:21 +02:00
|
|
|
unique_pins_(unique_pins),
|
2018-11-26 18:15:52 +01:00
|
|
|
div_queue_(DiversionGreater(sta)),
|
2018-09-28 17:54:21 +02:00
|
|
|
div_count_(0),
|
|
|
|
|
inserts_pruned_(false),
|
2019-03-13 01:25:53 +01:00
|
|
|
next_(nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathEnum::insert(PathEnd *path_end)
|
|
|
|
|
{
|
2021-01-01 20:46:51 +01:00
|
|
|
debugPrint(debug_, "path_enum", 1, "insert %s",
|
2025-03-31 00:27:53 +02:00
|
|
|
path_end->path()->to_string(this).c_str());
|
2021-01-01 20:46:51 +01:00
|
|
|
debugPrint(debug_, "path_enum", 2, "diversion %s %s %s",
|
2025-03-31 00:27:53 +02:00
|
|
|
path_end->path()->to_string(this).c_str(),
|
2021-01-01 20:46:51 +01:00
|
|
|
cmp_slack_ ? "slack" : "delay",
|
|
|
|
|
delayAsString(cmp_slack_ ? path_end->slack(this) :
|
|
|
|
|
path_end->dataArrivalTime(this), this));
|
2018-09-28 17:54:21 +02:00
|
|
|
Diversion *div = new Diversion(path_end, path_end->path());
|
|
|
|
|
div_queue_.push(div);
|
|
|
|
|
div_count_++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathEnum::~PathEnum()
|
|
|
|
|
{
|
|
|
|
|
while (!div_queue_.empty()) {
|
|
|
|
|
Diversion *div = div_queue_.top();
|
|
|
|
|
deleteDiversionPathEnd(div);
|
|
|
|
|
div_queue_.pop();
|
|
|
|
|
}
|
|
|
|
|
// PathEnd on deck may not have been consumed.
|
|
|
|
|
delete next_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
PathEnum::hasNext()
|
|
|
|
|
{
|
|
|
|
|
if (unique_pins_
|
|
|
|
|
&& !inserts_pruned_) {
|
|
|
|
|
pruneDiversionQueue();
|
|
|
|
|
inserts_pruned_ = true;
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
if (next_ == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
&& !div_queue_.empty())
|
|
|
|
|
findNext();
|
2019-03-13 01:25:53 +01:00
|
|
|
return next_ != nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathEnd *
|
|
|
|
|
PathEnum::next()
|
|
|
|
|
{
|
|
|
|
|
PathEnd *next = next_;
|
|
|
|
|
findNext();
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathEnum::findNext()
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
next_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
// Pop the next slowest path off the queue.
|
|
|
|
|
while (!div_queue_.empty()) {
|
|
|
|
|
Diversion *div = div_queue_.top();
|
|
|
|
|
div_queue_.pop();
|
|
|
|
|
PathEnd *path_end = div->pathEnd();
|
|
|
|
|
Vertex *vertex = path_end->vertex(this);
|
2025-04-23 20:38:44 +02:00
|
|
|
path_counts_[vertex]++;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (debug_->check("path_enum", 2)) {
|
|
|
|
|
Path *path = path_end->path();
|
2025-04-23 20:38:44 +02:00
|
|
|
report_->reportLine("path_enum: next path %zu %s delay %s slack %s",
|
|
|
|
|
path_counts_[vertex],
|
2025-03-31 00:27:53 +02:00
|
|
|
path->to_string(this).c_str(),
|
2020-12-29 19:33:22 +01:00
|
|
|
delayAsString(path_end->dataArrivalTime(this), this),
|
|
|
|
|
delayAsString(path_end->slack(this), this));
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDiversionPath(div);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-24 00:38:26 +01:00
|
|
|
if (path_counts_[vertex] <= endpoint_path_count_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
// Add diversions for all arcs converging on the path up to the
|
|
|
|
|
// diversion.
|
|
|
|
|
makeDiversions(path_end, div->divPath());
|
|
|
|
|
// Caller owns the path end now, so don't delete it.
|
|
|
|
|
next_ = path_end;
|
|
|
|
|
delete div;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-11-24 00:38:26 +01:00
|
|
|
// We have endpoint_path_count paths for this endpoint,
|
|
|
|
|
// so we are done with it.
|
|
|
|
|
debugPrint(debug_, "path_enum", 1,
|
|
|
|
|
"endpoint_path_count reached for %s",
|
2025-03-31 00:27:53 +02:00
|
|
|
vertex->to_string(this).c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
deleteDiversionPathEnd(div);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathEnum::reportDiversionPath(Diversion *div)
|
|
|
|
|
{
|
|
|
|
|
PathEnd *path_end = div->pathEnd();
|
|
|
|
|
Path *path = path_end->path();
|
2025-04-23 20:38:44 +02:00
|
|
|
Path *p = path_end->path();
|
2018-09-28 17:54:21 +02:00
|
|
|
Path *after_div = div->divPath();
|
2025-03-27 02:21:03 +01:00
|
|
|
while (p) {
|
2020-12-29 19:33:22 +01:00
|
|
|
report_->reportLine("path_enum: %s %s%s",
|
2025-03-31 00:27:53 +02:00
|
|
|
p->to_string(this).c_str(),
|
2025-03-27 02:21:03 +01:00
|
|
|
delayAsString(p->arrival(), this),
|
2025-04-23 20:38:44 +02:00
|
|
|
Path::equal(p, after_div, this) ? " <-after diversion" : "");
|
|
|
|
|
if (p != path && network_->isLatchData(p->pin(this)))
|
2018-09-28 17:54:21 +02:00
|
|
|
break;
|
2025-03-27 02:21:03 +01:00
|
|
|
p = p->prevPath();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2025-04-23 20:38:44 +02:00
|
|
|
typedef std::set<std::pair<const Vertex*, const TimingArc*>> VisitedFanins;
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
class PathEnumFaninVisitor : public PathVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
PathEnumFaninVisitor(PathEnd *path_end,
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *before_div,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
PathEnum *path_enum);
|
2021-12-16 03:35:02 +01:00
|
|
|
virtual VertexVisitor *copy() const;
|
2018-09-28 17:54:21 +02:00
|
|
|
virtual void visit(Vertex *) {} // Not used.
|
2025-03-27 02:21:03 +01:00
|
|
|
void visitFaninPathsThru(Path *before_div,
|
2018-09-28 17:54:21 +02:00
|
|
|
Vertex *prev_vertex,
|
|
|
|
|
TimingArc *prev_arc);
|
|
|
|
|
virtual bool visitFromToPath(const Pin *from_pin,
|
|
|
|
|
Vertex *from_vertex,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *from_rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
Tag *from_tag,
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *from_path,
|
2024-10-12 02:02:47 +02:00
|
|
|
const Arrival &from_arrival,
|
2018-09-28 17:54:21 +02:00
|
|
|
Edge *edge,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
ArcDelay arc_delay,
|
|
|
|
|
Vertex *to_vertex,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *to_rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
Tag *to_tag,
|
|
|
|
|
Arrival &to_arrival,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const PathAnalysisPt *path_ap);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void makeDivertedPathEnd(Path *after_div,
|
2025-03-27 02:21:03 +01:00
|
|
|
Edge *div_edge,
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingArc *div_arc,
|
|
|
|
|
// Return values.
|
|
|
|
|
PathEnd *&div_end,
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *&after_div_copy);
|
|
|
|
|
void reportDiversion(const Edge *edge,
|
|
|
|
|
const TimingArc *div_arc,
|
2018-09-28 17:54:21 +02:00
|
|
|
Path *after_div);
|
|
|
|
|
|
|
|
|
|
PathEnd *path_end_;
|
|
|
|
|
Slack path_end_slack_;
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *before_div_;
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins_;
|
|
|
|
|
Tag *before_div_tag_;
|
|
|
|
|
Arrival before_div_arrival_;
|
|
|
|
|
TimingArc *prev_arc_;
|
|
|
|
|
Vertex *prev_vertex_;
|
|
|
|
|
PathEnum *path_enum_;
|
|
|
|
|
bool crpr_active_;
|
2025-04-23 20:38:44 +02:00
|
|
|
VisitedFanins visited_fanins_;
|
2018-09-28 17:54:21 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end,
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *before_div,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
PathEnum *path_enum) :
|
|
|
|
|
PathVisitor(path_enum),
|
|
|
|
|
path_end_(path_end),
|
2022-08-12 03:17:46 +02:00
|
|
|
path_end_slack_(path_end->slack(this)),
|
2018-09-28 17:54:21 +02:00
|
|
|
before_div_(before_div),
|
|
|
|
|
unique_pins_(unique_pins),
|
2025-03-27 02:21:03 +01:00
|
|
|
before_div_tag_(before_div_->tag(this)),
|
|
|
|
|
before_div_arrival_(before_div_->arrival()),
|
2018-09-28 17:54:21 +02:00
|
|
|
path_enum_(path_enum),
|
2025-04-10 23:16:00 +02:00
|
|
|
crpr_active_(crprActive())
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2025-03-27 02:21:03 +01:00
|
|
|
PathEnumFaninVisitor::visitFaninPathsThru(Path *before_div,
|
2018-09-28 17:54:21 +02:00
|
|
|
Vertex *prev_vertex,
|
|
|
|
|
TimingArc *prev_arc)
|
|
|
|
|
{
|
2025-03-27 02:21:03 +01:00
|
|
|
before_div_ = before_div;
|
|
|
|
|
before_div_tag_ = before_div_->tag(this);
|
|
|
|
|
before_div_arrival_ = before_div_->arrival();
|
2018-09-28 17:54:21 +02:00
|
|
|
prev_arc_ = prev_arc;
|
|
|
|
|
prev_vertex_ = prev_vertex;
|
2025-04-23 20:38:44 +02:00
|
|
|
visited_fanins_.clear();
|
2025-03-27 02:21:03 +01:00
|
|
|
visitFaninPaths(before_div_->vertex(this));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VertexVisitor *
|
2021-12-16 03:35:02 +01:00
|
|
|
PathEnumFaninVisitor::copy() const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
return new PathEnumFaninVisitor(path_end_, before_div_, unique_pins_,
|
|
|
|
|
path_enum_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
PathEnumFaninVisitor::visitFromToPath(const Pin *,
|
|
|
|
|
Vertex *from_vertex,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *,
|
2018-09-28 17:54:21 +02:00
|
|
|
Tag *,
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *from_path,
|
2024-10-12 02:02:47 +02:00
|
|
|
const Arrival &,
|
2018-09-28 17:54:21 +02:00
|
|
|
Edge *edge,
|
|
|
|
|
TimingArc *arc,
|
|
|
|
|
ArcDelay,
|
|
|
|
|
Vertex *to_vertex,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *to_rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
Tag *to_tag,
|
|
|
|
|
Arrival &to_arrival,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const PathAnalysisPt *path_ap)
|
|
|
|
|
{
|
|
|
|
|
// These paths fanin to before_div_ so we know to_vertex matches.
|
2025-04-23 20:38:44 +02:00
|
|
|
if ((!unique_pins_ || from_vertex != prev_vertex_)
|
2018-09-28 17:54:21 +02:00
|
|
|
&& arc != prev_arc_
|
|
|
|
|
&& tagMatchNoCrpr(to_tag, before_div_tag_)) {
|
2025-04-23 20:38:44 +02:00
|
|
|
debugPrint(debug_, "path_enum", 3, "visit fanin %s -> %s %s %s",
|
|
|
|
|
from_path->to_string(this).c_str(),
|
|
|
|
|
to_vertex->to_string(this).c_str(),
|
|
|
|
|
to_rf->to_string().c_str(),
|
|
|
|
|
delayAsString(search_->deratedDelay(from_vertex, arc, edge,
|
|
|
|
|
false,path_ap), this));
|
2018-09-28 17:54:21 +02:00
|
|
|
if (crpr_active_) {
|
2025-04-23 20:38:44 +02:00
|
|
|
// Ingore paths that only differ by crpr from same vertex/edge.
|
|
|
|
|
if (visited_fanins_.find({from_vertex, arc}) == visited_fanins_.end()) {
|
|
|
|
|
PathEnd *div_end;
|
|
|
|
|
Path *after_div_copy;
|
|
|
|
|
// Make the diverted path end to check slack with from_path crpr.
|
|
|
|
|
makeDivertedPathEnd(from_path, edge, arc, div_end, after_div_copy);
|
|
|
|
|
if (div_end) {
|
|
|
|
|
// Only enumerate paths with greater slack.
|
|
|
|
|
if (div_end->slack(this) >= (path_end_slack_ * (1.0-.001))) {
|
|
|
|
|
reportDiversion(edge, arc, from_path);
|
|
|
|
|
path_enum_->makeDiversion(div_end, after_div_copy);
|
|
|
|
|
visited_fanins_.emplace(from_vertex, arc);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
delete div_end;
|
2025-02-26 23:44:38 +01:00
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2025-04-23 20:38:44 +02:00
|
|
|
else
|
|
|
|
|
debugPrint(debug_, "path_enum", 3, " pruned %s %s",
|
|
|
|
|
edge->to_string(this).c_str(),
|
|
|
|
|
arc->to_string().c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
// Only enumerate slower/faster paths.
|
2022-08-12 03:17:46 +02:00
|
|
|
else if (delayLessEqual(to_arrival, before_div_arrival_, min_max, this)) {
|
2018-09-28 17:54:21 +02:00
|
|
|
PathEnd *div_end;
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *after_div_copy;
|
|
|
|
|
makeDivertedPathEnd(from_path, edge, arc, div_end, after_div_copy);
|
|
|
|
|
reportDiversion(edge, arc, from_path);
|
2018-09-28 17:54:21 +02:00
|
|
|
path_enum_->makeDiversion(div_end, after_div_copy);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div,
|
2025-03-27 02:21:03 +01:00
|
|
|
Edge *div_edge,
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingArc *div_arc,
|
|
|
|
|
// Return values.
|
|
|
|
|
PathEnd *&div_end,
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *&after_div_copy)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *div_path;
|
|
|
|
|
path_enum_->makeDivertedPath(path_end_->path(), before_div_, after_div,
|
|
|
|
|
div_edge, div_arc, div_path, after_div_copy);
|
2025-02-26 23:44:38 +01:00
|
|
|
if (after_div_copy) {
|
|
|
|
|
div_end = path_end_->copy();
|
|
|
|
|
div_end->setPath(div_path);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
div_end = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2025-03-27 02:21:03 +01:00
|
|
|
PathEnumFaninVisitor::reportDiversion(const Edge *div_edge,
|
|
|
|
|
const TimingArc *div_arc,
|
2018-09-28 17:54:21 +02:00
|
|
|
Path *after_div)
|
|
|
|
|
{
|
2022-08-12 03:17:46 +02:00
|
|
|
if (debug_->check("path_enum", 3)) {
|
2018-09-28 17:54:21 +02:00
|
|
|
Path *path = path_end_->path();
|
2022-08-12 03:17:46 +02:00
|
|
|
const PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
Arrival path_delay = path_enum_->cmp_slack_
|
2022-08-12 03:17:46 +02:00
|
|
|
? path_end_->slack(this)
|
|
|
|
|
: path_end_->dataArrivalTime(this);
|
2025-03-27 02:21:03 +01:00
|
|
|
Arrival div_delay = path_delay - path_enum_->divSlack(before_div_,
|
|
|
|
|
after_div, div_edge,
|
|
|
|
|
div_arc, path_ap);
|
|
|
|
|
Path *div_prev = before_div_->prevPath();
|
2022-08-12 03:17:46 +02:00
|
|
|
report_->reportLine("path_enum: diversion %s %s %s -> %s",
|
2025-03-31 00:27:53 +02:00
|
|
|
path->to_string(this).c_str(),
|
2022-08-12 03:17:46 +02:00
|
|
|
path_enum_->cmp_slack_ ? "slack" : "delay",
|
|
|
|
|
delayAsString(path_delay, this),
|
|
|
|
|
delayAsString(div_delay, this));
|
|
|
|
|
report_->reportLine("path_enum: from %s -> %s",
|
2025-03-31 00:27:53 +02:00
|
|
|
div_prev->to_string(this).c_str(),
|
|
|
|
|
before_div_->to_string(this).c_str());
|
2024-07-20 02:03:03 +02:00
|
|
|
report_->reportLine("path_enum: to %s",
|
2025-03-31 00:27:53 +02:00
|
|
|
after_div->to_string(this).c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A diversion is an alternate path formed by changing the previous
|
|
|
|
|
// path/arc of before_div to after_div/div_arc in path.
|
|
|
|
|
//
|
|
|
|
|
// div_arc
|
|
|
|
|
// after_div<--------+
|
|
|
|
|
// |
|
|
|
|
|
// <--...--before_div<--...--path<---path_end
|
|
|
|
|
void
|
|
|
|
|
PathEnum::makeDiversion(PathEnd *div_end,
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *after_div_copy)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
Diversion *div = new Diversion(div_end, after_div_copy);
|
|
|
|
|
div_queue_.push(div);
|
|
|
|
|
div_count_++;
|
|
|
|
|
|
2025-03-27 02:21:03 +01:00
|
|
|
if (div_queue_.size() > group_path_count_ * 2)
|
2018-09-28 17:54:21 +02:00
|
|
|
// We have more potenial paths than we will need.
|
|
|
|
|
pruneDiversionQueue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathEnum::pruneDiversionQueue()
|
|
|
|
|
{
|
2021-03-13 01:36:13 +01:00
|
|
|
debugPrint(debug_, "path_enum", 2, "prune queue");
|
2018-09-28 17:54:21 +02:00
|
|
|
VertexPathCountMap path_counts;
|
2025-03-27 02:21:03 +01:00
|
|
|
size_t end_count = 0;
|
2024-11-24 00:38:26 +01:00
|
|
|
// Collect endpoint_path_count diversions per vertex.
|
2018-09-28 17:54:21 +02:00
|
|
|
DiversionSeq divs;
|
|
|
|
|
while (!div_queue_.empty()) {
|
|
|
|
|
Diversion *div = div_queue_.top();
|
|
|
|
|
Vertex *vertex = div->pathEnd()->vertex(this);
|
2024-11-24 00:38:26 +01:00
|
|
|
if (end_count < group_path_count_
|
2018-09-28 17:54:21 +02:00
|
|
|
&& ((unique_pins_ && path_counts[vertex] == 0)
|
2024-11-24 00:38:26 +01:00
|
|
|
|| (!unique_pins_ && path_counts[vertex] < endpoint_path_count_))) {
|
2018-09-28 17:54:21 +02:00
|
|
|
divs.push_back(div);
|
|
|
|
|
path_counts[vertex]++;
|
|
|
|
|
end_count++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
deleteDiversionPathEnd(div);
|
|
|
|
|
div_queue_.pop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the top diversions back.
|
2025-04-23 20:38:44 +02:00
|
|
|
for (Diversion *div : divs)
|
2018-09-28 17:54:21 +02:00
|
|
|
div_queue_.push(div);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Arrival
|
|
|
|
|
PathEnum::divSlack(Path *before_div,
|
|
|
|
|
Path *after_div,
|
2025-03-27 02:21:03 +01:00
|
|
|
const Edge *div_edge,
|
|
|
|
|
const TimingArc *div_arc,
|
2018-09-28 17:54:21 +02:00
|
|
|
const PathAnalysisPt *path_ap)
|
|
|
|
|
{
|
2025-03-27 02:21:03 +01:00
|
|
|
Arrival arc_arrival = before_div->arrival();
|
2022-01-15 20:51:05 +01:00
|
|
|
if (div_edge) {
|
|
|
|
|
ArcDelay div_delay = search_->deratedDelay(div_edge->from(graph_),
|
|
|
|
|
div_arc, div_edge,
|
|
|
|
|
false, path_ap);
|
|
|
|
|
Arrival div_arrival = search_->clkPathArrival(after_div) + div_delay;
|
|
|
|
|
return div_arrival - arc_arrival;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-01-08 03:23:53 +01:00
|
|
|
report()->error(1370, "path diversion missing edge.");
|
2022-01-15 20:51:05 +01:00
|
|
|
return 0.0;
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make diversions for all arcs that merge into path for paths
|
|
|
|
|
// starting at "before" to the beginning of the path.
|
|
|
|
|
void
|
|
|
|
|
PathEnum::makeDiversions(PathEnd *path_end,
|
|
|
|
|
Path *before)
|
|
|
|
|
{
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *path = before;
|
|
|
|
|
Path *prev_path = path->prevPath();
|
|
|
|
|
TimingArc *prev_arc = path->prevArc(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
PathEnumFaninVisitor fanin_visitor(path_end, path, unique_pins_, this);
|
2025-04-10 17:48:55 +02:00
|
|
|
while (prev_path) {
|
2018-09-28 17:54:21 +02:00
|
|
|
// Fanin visitor does all the work.
|
|
|
|
|
// While visiting the fanins the fanin_visitor finds the
|
|
|
|
|
// previous path and arc as well as diversions.
|
2025-03-27 02:21:03 +01:00
|
|
|
fanin_visitor.visitFaninPathsThru(path, prev_path->vertex(this), prev_arc);
|
2024-07-20 02:03:03 +02:00
|
|
|
// Do not enumerate beyond latch D to Q edges.
|
|
|
|
|
// This breaks latch loop paths.
|
2025-04-10 17:48:55 +02:00
|
|
|
const TimingRole *prev_role = prev_arc->role();
|
|
|
|
|
if (prev_role == TimingRole::latchDtoQ()
|
|
|
|
|
|| prev_role == TimingRole::regClkToQ())
|
2024-07-20 02:03:03 +02:00
|
|
|
break;
|
2025-03-27 02:21:03 +01:00
|
|
|
path = prev_path;
|
|
|
|
|
prev_path = path->prevPath();
|
|
|
|
|
prev_arc = path->prevArc(this);
|
2023-07-17 02:49:09 +02:00
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathEnum::makeDivertedPath(Path *path,
|
|
|
|
|
Path *before_div,
|
|
|
|
|
Path *after_div,
|
2025-03-27 02:21:03 +01:00
|
|
|
Edge *div_edge,
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingArc *div_arc,
|
|
|
|
|
// Returned values.
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *&div_path,
|
|
|
|
|
Path *&after_div_copy)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2025-02-26 23:44:38 +01:00
|
|
|
div_path = nullptr;
|
|
|
|
|
after_div_copy = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
// Copy the diversion path.
|
|
|
|
|
bool found_div = false;
|
2025-03-27 02:21:03 +01:00
|
|
|
PathSeq copies;
|
|
|
|
|
Path *p = path;
|
2018-09-28 17:54:21 +02:00
|
|
|
bool first = true;
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *prev_copy = nullptr;
|
|
|
|
|
while (p) {
|
|
|
|
|
// prev_path made in next pass.
|
|
|
|
|
Path *copy = new Path(p->vertex(this),
|
|
|
|
|
p->tag(this),
|
|
|
|
|
p->arrival(),
|
|
|
|
|
// Replaced on next pass.
|
|
|
|
|
p->prevPath(),
|
|
|
|
|
p->prevEdge(this),
|
|
|
|
|
p->prevArc(this),
|
|
|
|
|
true, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (prev_copy)
|
|
|
|
|
prev_copy->setPrevPath(copy);
|
|
|
|
|
copies.push_back(copy);
|
|
|
|
|
|
2025-04-08 01:25:02 +02:00
|
|
|
if (p == after_div)
|
2018-09-28 17:54:21 +02:00
|
|
|
after_div_copy = copy;
|
2025-02-26 23:44:38 +01:00
|
|
|
if (first)
|
|
|
|
|
div_path = copy;
|
2025-03-27 02:21:03 +01:00
|
|
|
else if (network_->isLatchData(p->pin(this)))
|
2025-02-26 23:44:38 +01:00
|
|
|
break;
|
2025-04-08 01:25:02 +02:00
|
|
|
if (p == before_div) {
|
2025-03-27 02:21:03 +01:00
|
|
|
// Replaced on next pass.
|
|
|
|
|
copy->setPrevPath(after_div);
|
|
|
|
|
copy->setPrevEdgeArc(div_edge, div_arc, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
// Update the delays forward from before_div to the end of the path.
|
|
|
|
|
updatePathHeadDelays(copies, after_div);
|
2025-03-27 02:21:03 +01:00
|
|
|
p = after_div;
|
2018-09-28 17:54:21 +02:00
|
|
|
found_div = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
2025-03-27 02:21:03 +01:00
|
|
|
p = p->prevPath();
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
prev_copy = copy;
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
if (!found_div)
|
2024-02-11 19:45:40 +01:00
|
|
|
criticalError(280, "diversion path not found");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2025-03-27 02:21:03 +01:00
|
|
|
PathEnum::updatePathHeadDelays(PathSeq &paths,
|
2018-09-28 17:54:21 +02:00
|
|
|
Path *after_div)
|
|
|
|
|
{
|
|
|
|
|
Tag *prev_tag = after_div->tag(this);
|
2024-07-20 02:03:03 +02:00
|
|
|
ClkInfo *prev_clk_info = prev_tag->clkInfo();
|
2018-09-28 17:54:21 +02:00
|
|
|
Arrival prev_arrival = search_->clkPathArrival(after_div);
|
|
|
|
|
for (int i = paths.size() - 1; i >= 0; i--) {
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *path = paths[i];
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingArc *arc = path->prevArc(this);
|
2025-03-27 02:21:03 +01:00
|
|
|
Edge *edge = path->prevEdge(this);
|
2022-01-15 20:51:05 +01:00
|
|
|
if (edge) {
|
|
|
|
|
PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
|
|
|
|
|
ArcDelay arc_delay = search_->deratedDelay(edge->from(graph_),
|
|
|
|
|
arc, edge, false, path_ap);
|
|
|
|
|
Arrival arrival = prev_arrival + arc_delay;
|
2025-02-26 23:44:38 +01:00
|
|
|
debugPrint(debug_, "path_enum", 5, "update arrival %s %s %s -> %s",
|
2025-03-31 00:27:53 +02:00
|
|
|
path->vertex(this)->to_string(this).c_str(),
|
|
|
|
|
path->tag(this)->to_string(this).c_str(),
|
2025-03-27 02:21:03 +01:00
|
|
|
delayAsString(path->arrival(), this),
|
2022-01-15 20:51:05 +01:00
|
|
|
delayAsString(arrival, this));
|
2025-03-27 02:21:03 +01:00
|
|
|
path->setArrival(arrival);
|
2022-01-15 20:51:05 +01:00
|
|
|
prev_arrival = arrival;
|
2025-04-08 01:25:02 +02:00
|
|
|
const Tag *tag = path->tag(this);
|
|
|
|
|
const ClkInfo *clk_info = tag->clkInfo();
|
2025-04-10 23:16:00 +02:00
|
|
|
if (crprActive()
|
2025-04-08 01:25:02 +02:00
|
|
|
&& clk_info != prev_clk_info
|
2024-07-20 02:03:03 +02:00
|
|
|
// D->Q paths use the EN->Q clk info so no need to update.
|
|
|
|
|
&& arc->role() != TimingRole::latchDtoQ()) {
|
2022-01-15 20:51:05 +01:00
|
|
|
// When crpr is enabled the diverion may be from another crpr clk pin,
|
|
|
|
|
// so update the tags to use the corresponding ClkInfo.
|
|
|
|
|
Tag *updated_tag = search_->findTag(path->transition(this),
|
|
|
|
|
path_ap,
|
2024-07-20 02:03:03 +02:00
|
|
|
prev_clk_info,
|
2022-01-15 20:51:05 +01:00
|
|
|
tag->isClock(),
|
|
|
|
|
tag->inputDelay(),
|
|
|
|
|
tag->isSegmentStart(),
|
|
|
|
|
tag->states(), false);
|
|
|
|
|
path->setTag(updated_tag);
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|