OpenSTA/search/PathEnum.cc

615 lines
17 KiB
C++
Raw Normal View History

2018-09-28 17:54:21 +02:00
// OpenSTA, Static Timing Analyzer
2019-01-01 21:26:11 +01:00
// Copyright (c) 2019, 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
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Machine.hh"
#include "DisallowCopyAssign.hh"
#include "Debug.hh"
#include "Error.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"
#include "PathRef.hh"
#include "PathEnumed.hh"
#include "PathEnum.hh"
#include "Fuzzy.hh"
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:
DISALLOW_COPY_AND_ASSIGN(Diversion);
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() :
2018-11-26 18:15:52 +01:00
sta_(NULL)
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;
}
////////////////////////////////////////////////////////////////
2018-10-24 01:28:41 +02:00
PathEnum::PathEnum(int group_count,
int endpoint_count,
2018-09-28 17:54:21 +02:00
bool unique_pins,
bool cmp_slack,
const StaState *sta) :
StaState(sta),
cmp_slack_(cmp_slack),
2018-10-24 01:28:41 +02:00
group_count_(group_count),
endpoint_count_(endpoint_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),
next_(NULL)
{
}
void
PathEnum::insert(PathEnd *path_end)
{
debugPrint1(debug_, "path_enum", 1, "insert %s\n",
path_end->path()->name(this));
debugPrint3(debug_, "path_enum", 2, "diversion %s %s %s\n",
path_end->path()->name(this),
cmp_slack_ ? "slack" : "delay",
delayAsString(cmp_slack_ ? path_end->slack(this) :
2019-01-27 08:03:01 +01:00
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;
}
if (next_ == NULL
&& !div_queue_.empty())
findNext();
return next_ != NULL;
}
PathEnd *
PathEnum::next()
{
PathEnd *next = next_;
findNext();
return next;
}
void
PathEnum::findNext()
{
next_ = NULL;
// 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);
if (debug_->check("path_enum", 2)) {
Path *path = path_end->path();
debug_->print("path_enum: next path %s delay %s slack %s\n",
path->name(this),
2019-01-27 08:03:01 +01:00
delayAsString(path_end->dataArrivalTime(this), this),
delayAsString(path_end->slack(this), this));
2018-09-28 17:54:21 +02:00
reportDiversionPath(div);
}
path_counts_[vertex]++;
2018-10-24 01:28:41 +02:00
if (path_counts_[vertex] <= endpoint_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 {
2018-10-24 01:28:41 +02:00
// We have endpoint_count paths for this endpoint, so we are done with it.
debugPrint1(debug_, "path_enum", 1, "endpoint_count reached for %s\n",
2018-09-28 17:54:21 +02:00
vertex->name(sdc_network_));
deleteDiversionPathEnd(div);
}
}
}
void
PathEnum::reportDiversionPath(Diversion *div)
{
PathEnd *path_end = div->pathEnd();
Path *path = path_end->path();
PathRef p;
path->prevPath(this, p);
Path *after_div = div->divPath();
while (!p.isNull()) {
debug_->print("path_enum: %s %s%s\n",
p.name(this),
2019-01-27 08:03:01 +01:00
delayAsString(p.arrival(this), this),
2018-09-28 17:54:21 +02:00
Path::equal(&p, after_div, this) ? " <-diversion" : "");
if (network_->isLatchData(p.pin(this)))
break;
p.prevPath(this, p);
}
}
////////////////////////////////////////////////////////////////
class PathEnumFaninVisitor : public PathVisitor
{
public:
PathEnumFaninVisitor(PathEnd *path_end,
PathRef &before_div,
bool unique_pins,
PathEnum *path_enum);
virtual VertexVisitor *copy();
virtual void visit(Vertex *) {} // Not used.
void visitFaninPathsThru(Vertex *vertex,
Vertex *prev_vertex,
TimingArc *prev_arc);
virtual bool visitFromToPath(const Pin *from_pin,
Vertex *from_vertex,
const TransRiseFall *from_tr,
Tag *from_tag,
PathVertex *from_path,
Edge *edge,
TimingArc *arc,
ArcDelay arc_delay,
Vertex *to_vertex,
const TransRiseFall *to_tr,
Tag *to_tag,
Arrival &to_arrival,
const MinMax *min_max,
const PathAnalysisPt *path_ap);
private:
DISALLOW_COPY_AND_ASSIGN(PathEnumFaninVisitor);
void makeDivertedPathEnd(Path *after_div,
TimingArc *div_arc,
// Return values.
PathEnd *&div_end,
PathEnumed *&after_div_copy);
void reportDiversion(TimingArc *div_arc,
Path *after_div);
PathEnd *path_end_;
Slack path_end_slack_;
PathRef &before_div_;
bool unique_pins_;
int before_div_tr_index_;
Tag *before_div_tag_;
PathAPIndex before_div_ap_index_;
Arrival before_div_arrival_;
TimingArc *prev_arc_;
Vertex *prev_vertex_;
PathEnum *path_enum_;
bool crpr_active_;
};
PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end,
PathRef &before_div,
bool unique_pins,
PathEnum *path_enum) :
PathVisitor(path_enum),
path_end_(path_end),
path_end_slack_(path_end->slack(sta_)),
before_div_(before_div),
unique_pins_(unique_pins),
before_div_tr_index_(before_div_.trIndex(sta_)),
before_div_tag_(before_div_.tag(sta_)),
before_div_ap_index_(before_div_.pathAnalysisPtIndex(sta_)),
before_div_arrival_(before_div_.arrival(sta_)),
path_enum_(path_enum),
crpr_active_(sta_->sdc()->crprActive())
{
}
void
PathEnumFaninVisitor::visitFaninPathsThru(Vertex *vertex,
Vertex *prev_vertex,
TimingArc *prev_arc)
{
before_div_tr_index_ = before_div_.trIndex(sta_);
before_div_tag_ = before_div_.tag(sta_);
before_div_ap_index_ = before_div_.pathAnalysisPtIndex(sta_);
before_div_arrival_ = before_div_.arrival(sta_);
prev_arc_ = prev_arc;
prev_vertex_ = prev_vertex;
visitFaninPaths(vertex);
}
VertexVisitor *
PathEnumFaninVisitor::copy()
{
return new PathEnumFaninVisitor(path_end_, before_div_, unique_pins_,
path_enum_);
}
bool
PathEnumFaninVisitor::visitFromToPath(const Pin *,
Vertex *from_vertex,
const TransRiseFall *,
Tag *,
PathVertex *from_path,
Edge *edge,
TimingArc *arc,
ArcDelay,
Vertex *to_vertex,
const TransRiseFall *to_tr,
Tag *to_tag,
Arrival &to_arrival,
const MinMax *min_max,
const PathAnalysisPt *path_ap)
{
const Debug *debug = sta_->debug();
debugPrint4(debug, "path_enum", 3, "visit fanin %s -> %s %s %s\n",
from_path->name(sta_),
to_vertex->name(sta_->network()),
to_tr->asString(),
delayAsString(sta_->search()->deratedDelay(from_vertex, arc, edge,
false,path_ap),sta_));
// These paths fanin to before_div_ so we know to_vertex matches.
if (to_tr->index() == before_div_tr_index_
&& path_ap->index() == before_div_ap_index_
&& arc != prev_arc_
&& (!unique_pins_ || from_vertex != prev_vertex_)
&& tagMatchNoCrpr(to_tag, before_div_tag_)) {
if (crpr_active_) {
PathEnd *div_end;
PathEnumed *after_div_copy;
// Make the diverted path end to check slack with from_path crpr.
makeDivertedPathEnd(from_path, arc, div_end, after_div_copy);
// Only enumerate paths with greater slack.
if (delayFuzzyGreaterEqual(div_end->slack(sta_), path_end_slack_)) {
reportDiversion(arc, from_path);
path_enum_->makeDiversion(div_end, after_div_copy);
}
else
delete div_end;
}
// Only enumerate slower/faster paths.
else if (delayFuzzyLessEqual(to_arrival, before_div_arrival_, min_max)) {
PathEnd *div_end;
PathEnumed *after_div_copy;
makeDivertedPathEnd(from_path, arc, div_end, after_div_copy);
reportDiversion(arc, from_path);
path_enum_->makeDiversion(div_end, after_div_copy);
}
}
return true;
}
void
PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div,
TimingArc *div_arc,
// Return values.
PathEnd *&div_end,
PathEnumed *&after_div_copy)
{
PathEnumed *div_path;
path_enum_->makeDivertedPath(path_end_->path(), &before_div_, after_div,
div_arc, div_path, after_div_copy);
div_end = path_end_->copy();
div_end->setPath(div_path, sta_);
}
void
PathEnumFaninVisitor::reportDiversion(TimingArc *div_arc,
Path *after_div)
{
Debug *debug = sta_->debug();
if (debug->check("path_enum", 3)) {
Path *path = path_end_->path();
const PathAnalysisPt *path_ap = path->pathAnalysisPt(sta_);
Arrival path_delay = path_enum_->cmp_slack_
? path_end_->slack(sta_)
: path_end_->dataArrivalTime(sta_);
Arrival div_delay = path_delay - path_enum_->divSlack(&before_div_,
after_div,
div_arc, path_ap);
PathRef div_prev;
before_div_.prevPath(sta_, div_prev);
debug->print("path_enum: diversion %s %s %s -> %s\n",
path->name(sta_),
path_enum_->cmp_slack_ ? "slack" : "delay",
2019-01-27 08:03:01 +01:00
delayAsString(path_delay, sta_),
delayAsString(div_delay, sta_));
2018-09-28 17:54:21 +02:00
debug->print("path_enum: from %s -> %s\n",
div_prev.name(sta_),
before_div_.name(sta_));
debug->print("path_enum: to %s ->\n",
after_div->name(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
void
PathEnum::makeDiversion(PathEnd *div_end,
PathEnumed *after_div_copy)
{
Diversion *div = new Diversion(div_end, after_div_copy);
div_queue_.push(div);
div_count_++;
2018-10-24 01:28:41 +02:00
if (static_cast<int>(div_queue_.size()) > group_count_ * 2)
2018-09-28 17:54:21 +02:00
// We have more potenial paths than we will need.
pruneDiversionQueue();
}
void
PathEnum::pruneDiversionQueue()
{
debugPrint0(debug_, "path_enum", 2, "prune queue\n");
VertexPathCountMap path_counts;
int end_count = 0;
2018-10-24 01:28:41 +02:00
// Collect endpoint_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);
2018-10-24 01:28:41 +02:00
if (end_count < group_count_
2018-09-28 17:54:21 +02:00
&& ((unique_pins_ && path_counts[vertex] == 0)
2018-10-24 01:28:41 +02:00
|| (!unique_pins_ && path_counts[vertex] < endpoint_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.
DiversionSeq::Iterator div_iter(divs);
while (div_iter.hasNext()) {
Diversion *div = div_iter.next();
div_queue_.push(div);
}
}
Arrival
PathEnum::divSlack(Path *before_div,
Path *after_div,
TimingArc *div_arc,
const PathAnalysisPt *path_ap)
{
Arrival arc_arrival = before_div->arrival(this);
Edge *div_edge = divEdge(before_div, div_arc);
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;
}
Edge *
PathEnum::divEdge(Path *before_div,
TimingArc *div_arc)
{
TimingArcSet *arc_set = div_arc->set();
VertexInEdgeIterator edge_iter(before_div->vertex(this), graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->timingArcSet() == arc_set)
return edge;
}
return NULL;
}
// 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)
{
PathRef path(before);
TimingArc *prev_arc;
PathEnumFaninVisitor fanin_visitor(path_end, path, unique_pins_, this);
do {
// Fanin visitor does all the work.
// While visiting the fanins the fanin_visitor finds the
// previous path and arc as well as diversions.
PathRef prev_path;
path.prevPath(this, prev_path, prev_arc);
if (prev_arc) {
fanin_visitor.visitFaninPathsThru(path.vertex(this),
prev_path.vertex(this), prev_arc);
path.init(prev_path);
}
} while (prev_arc
// Do not enumerate beyond latch D to Q edges.
// This breaks latch loop paths.
&& prev_arc->role() != TimingRole::latchDtoQ()
// Do not enumerate paths in the clk network.
&& !path.isClock(this));
}
void
PathEnum::makeDivertedPath(Path *path,
Path *before_div,
Path *after_div,
TimingArc *div_arc,
// Returned values.
PathEnumed *&div_path,
PathEnumed *&after_div_copy)
{
// Copy the diversion path.
bool found_div = false;
PathEnumedSeq copies;
PathRef p(path);
bool first = true;
bool is_latch_data = false;
PathEnumed *prev_copy = NULL;
while (!p.isNull()) {
PathRef prev;
TimingArc *prev_arc;
p.prevPath(this, prev, prev_arc);
PathEnumed *copy = new PathEnumed(p.vertexIndex(this),
p.tagIndex(this),
p.arrival(this),
NULL, // prev_path made in next pass.
prev_arc);
if (prev_copy)
prev_copy->setPrevPath(copy);
copies.push_back(copy);
if (first)
div_path = copy;
if (Path::equal(&p, after_div, this))
after_div_copy = copy;
// Include latch D input in the diverted path but do not enumerate
// beyond it.
if (is_latch_data)
break;
if (Path::equal(&p, before_div, this)) {
copy->setPrevArc(div_arc);
// Update the delays forward from before_div to the end of the path.
updatePathHeadDelays(copies, after_div);
p.init(after_div);
found_div = true;
}
else
p.init(prev);
prev_copy = copy;
first = false;
if (prev_arc
&& prev_arc->role() == TimingRole::latchDtoQ())
is_latch_data = true;
}
if (!found_div)
internalError("diversion path not found");
}
void
PathEnum::updatePathHeadDelays(PathEnumedSeq &paths,
Path *after_div)
{
Tag *prev_tag = after_div->tag(this);
ClkInfo *clk_info = prev_tag->clkInfo();
Arrival prev_arrival = search_->clkPathArrival(after_div);
for (int i = paths.size() - 1; i >= 0; i--) {
PathEnumed *path = paths[i];
TimingArc *arc = path->prevArc(this);
Edge *edge = path->prevEdge(arc, this);
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;
debugPrint3(debug_, "path_enum", 3, "update arrival %s %s -> %s\n",
path->name(this),
2019-01-27 08:03:01 +01:00
delayAsString(path->arrival(this), this),
delayAsString(arrival, this));
2018-09-28 17:54:21 +02:00
path->setArrival(arrival, this);
prev_arrival = arrival;
if (sdc_->crprActive()) {
// When crpr is enabled the diverion may be from another crpr clk pin,
// so update the tags to use the corresponding ClkInfo.
Tag *tag = path->tag(this);
Tag *updated_tag = search_->findTag(path->transition(this),
path_ap,
clk_info,
tag->isClock(),
tag->inputDelay(),
tag->isSegmentStart(),
tag->states(), false);
path->setTag(updated_tag);
}
}
}
} // namespace