370 lines
7.8 KiB
C++
370 lines
7.8 KiB
C++
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2024, Parallax Software, Inc.
|
|
//
|
|
// 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 "Path.hh"
|
|
|
|
#include "TimingRole.hh"
|
|
#include "TimingArc.hh"
|
|
#include "Network.hh"
|
|
#include "Graph.hh"
|
|
#include "Clock.hh"
|
|
#include "DcalcAnalysisPt.hh"
|
|
#include "Corner.hh"
|
|
#include "PathAnalysisPt.hh"
|
|
#include "Tag.hh"
|
|
#include "PathRef.hh"
|
|
|
|
namespace sta {
|
|
|
|
const char *
|
|
Path::name(const StaState *sta) const
|
|
{
|
|
const Network *network = sta->network();
|
|
Vertex *vertex1 = vertex(sta);
|
|
if (vertex1) {
|
|
const char *vertex_name = vertex1->name(network);
|
|
const char *tr_str = transition(sta)->asString();
|
|
const PathAnalysisPt *path_ap = pathAnalysisPt(sta);
|
|
int ap_index = path_ap->index();
|
|
const char *min_max = path_ap->pathMinMax()->asString();
|
|
TagIndex tag_index = tagIndex(sta);
|
|
return stringPrintTmp("%s %s %s/%d %d",
|
|
vertex_name, tr_str, min_max,
|
|
ap_index, tag_index);
|
|
}
|
|
else
|
|
return "NULL";
|
|
}
|
|
|
|
Pin *
|
|
Path::pin(const StaState *sta) const
|
|
{
|
|
return vertex(sta)->pin();
|
|
}
|
|
|
|
TagIndex
|
|
Path::tagIndex(const StaState *sta) const
|
|
{
|
|
return tag(sta)->index();
|
|
}
|
|
|
|
ClkInfo *
|
|
Path::clkInfo(const StaState *sta) const
|
|
{
|
|
return tag(sta)->clkInfo();
|
|
}
|
|
|
|
const ClockEdge *
|
|
Path::clkEdge(const StaState *sta) const
|
|
{
|
|
return tag(sta)->clkEdge();
|
|
}
|
|
|
|
const Clock *
|
|
Path::clock(const StaState *sta) const
|
|
{
|
|
return tag(sta)->clock();
|
|
}
|
|
|
|
bool
|
|
Path::isClock(const StaState *sta) const
|
|
{
|
|
return tag(sta)->isClock();
|
|
}
|
|
|
|
const MinMax *
|
|
Path::minMax(const StaState *sta) const
|
|
{
|
|
return pathAnalysisPt(sta)->pathMinMax();
|
|
}
|
|
|
|
PathAPIndex
|
|
Path::pathAnalysisPtIndex(const StaState *sta) const
|
|
{
|
|
return pathAnalysisPt(sta)->index();
|
|
}
|
|
|
|
DcalcAnalysisPt *
|
|
Path::dcalcAnalysisPt(const StaState *sta) const
|
|
{
|
|
return pathAnalysisPt(sta)->dcalcAnalysisPt();
|
|
}
|
|
|
|
Slew
|
|
Path::slew(const StaState *sta) const
|
|
{
|
|
return sta->graph()->slew(vertex(sta), transition(sta),
|
|
dcalcAnalysisPt(sta)->index());
|
|
}
|
|
|
|
int
|
|
Path::rfIndex(const StaState *sta) const
|
|
{
|
|
return transition(sta)->index();
|
|
}
|
|
|
|
void
|
|
Path::initArrival(const StaState *sta)
|
|
{
|
|
setArrival(delayInitValue(minMax(sta)), sta);
|
|
}
|
|
|
|
bool
|
|
Path::arrivalIsInitValue(const StaState *sta) const
|
|
{
|
|
return delayIsInitValue(arrival(sta), minMax(sta));
|
|
}
|
|
|
|
void
|
|
Path::initRequired(const StaState *sta)
|
|
{
|
|
setRequired(delayInitValue(minMax(sta)->opposite()), sta);
|
|
}
|
|
|
|
bool
|
|
Path::requiredIsInitValue(const StaState *sta) const
|
|
{
|
|
return delayIsInitValue(required(sta), minMax(sta)->opposite());
|
|
}
|
|
|
|
Slack
|
|
Path::slack(const StaState *sta) const
|
|
{
|
|
if (minMax(sta) == MinMax::max())
|
|
return required(sta) - arrival(sta);
|
|
else
|
|
return arrival(sta) - required(sta);
|
|
}
|
|
|
|
void
|
|
Path::prevPath(const StaState *sta,
|
|
// Return values.
|
|
PathRef &prev_path) const
|
|
{
|
|
TimingArc *prev_arc;
|
|
prevPath(sta, prev_path, prev_arc);
|
|
}
|
|
|
|
TimingArc *
|
|
Path::prevArc(const StaState *sta) const
|
|
{
|
|
PathRef prev_path;
|
|
TimingArc *prev_arc;
|
|
prevPath(sta, prev_path, prev_arc);
|
|
return prev_arc;
|
|
}
|
|
|
|
Edge *
|
|
Path::prevEdge(const TimingArc *prev_arc,
|
|
const StaState *sta) const
|
|
{
|
|
if (prev_arc) {
|
|
TimingArcSet *arc_set = prev_arc->set();
|
|
VertexInEdgeIterator edge_iter(vertex(sta), sta->graph());
|
|
while (edge_iter.hasNext()) {
|
|
Edge *edge = edge_iter.next();
|
|
if (edge->timingArcSet() == arc_set)
|
|
return edge;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
Path::cmpPinTrClk(const Path *path1,
|
|
const Path *path2,
|
|
const StaState *sta)
|
|
{
|
|
if (path1 && path2) {
|
|
const Pin *pin1 = path1->pin(sta);
|
|
const Pin *pin2 = path2->pin(sta);
|
|
const Network *network = sta->network();
|
|
if (pin1 == pin2) {
|
|
int tr_index1 = path1->rfIndex(sta);
|
|
int tr_index2 = path2->rfIndex(sta);
|
|
if (tr_index1 == tr_index2)
|
|
return cmpClk(path1, path2, sta);
|
|
else if (tr_index1 < tr_index2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
else if (network->pathNameLess(pin1, pin2))
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
else if (path1 == nullptr && path2 == nullptr)
|
|
return 0;
|
|
else if (path1 == nullptr)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
Path::cmpClk(const Path *path1,
|
|
const Path *path2,
|
|
const StaState *sta)
|
|
{
|
|
const ClockEdge *clk_edge1 = path1->clkEdge(sta);
|
|
const ClockEdge *clk_edge2 = path2->clkEdge(sta);
|
|
if (clk_edge1 && clk_edge2) {
|
|
int index1 = clk_edge1->index();
|
|
int index2 = clk_edge2->index();
|
|
if (index1 == index2)
|
|
return 0;
|
|
else if (index1 < index2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
else if (clk_edge1 == nullptr
|
|
&& clk_edge2 == nullptr)
|
|
return 0;
|
|
else if (clk_edge2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
bool
|
|
Path::equal(const Path *path1,
|
|
const Path *path2,
|
|
const StaState *sta)
|
|
{
|
|
bool path1_null = (path1 == nullptr || path1->isNull());
|
|
bool path2_null = (path2 == nullptr || path2->isNull());
|
|
return (path1_null && path2_null)
|
|
|| (!path1_null
|
|
&& !path2_null
|
|
&& path1->vertexId(sta) == path2->vertexId(sta)
|
|
// Tag equal implies transition and path ap equal.
|
|
&& path1->tagIndex(sta) == path2->tagIndex(sta));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
PathLess::PathLess(const StaState *sta) :
|
|
sta_(sta)
|
|
{
|
|
}
|
|
|
|
bool
|
|
PathLess::operator()(const Path *path1,
|
|
const Path *path2) const
|
|
{
|
|
return Path::less(path1, path2, sta_);
|
|
}
|
|
|
|
bool
|
|
Path::less(const Path *path1,
|
|
const Path *path2,
|
|
const StaState *sta)
|
|
{
|
|
return cmp(path1, path2, sta) < 0;
|
|
}
|
|
|
|
int
|
|
Path::cmp(const Path *path1,
|
|
const Path *path2,
|
|
const StaState *sta)
|
|
{
|
|
if (path1 && path2) {
|
|
VertexId vertex_id1 = path1->vertexId(sta);
|
|
VertexId vertex_id2 = path2->vertexId(sta);
|
|
if (vertex_id1 == vertex_id2) {
|
|
TagIndex tag_index1 = path1->tagIndex(sta);
|
|
TagIndex tag_index2 = path2->tagIndex(sta);
|
|
if (tag_index1 == tag_index2)
|
|
return 0;
|
|
else if (tag_index1 < tag_index2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
else if (vertex_id1 < vertex_id2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
else if (path1 == nullptr
|
|
&& path2 == nullptr)
|
|
return 0;
|
|
else if (path1 == nullptr)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
Path::cmpNoCrpr(const Path *path1,
|
|
const Path *path2,
|
|
const StaState *sta)
|
|
{
|
|
VertexId vertex_id1 = path1->vertexId(sta);
|
|
VertexId vertex_id2 = path2->vertexId(sta);
|
|
if (vertex_id1 == vertex_id2)
|
|
return tagMatchCmp(path1->tag(sta), path2->tag(sta), false, sta);
|
|
else if (vertex_id1 < vertex_id2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
Path::cmpAll(const Path *path1,
|
|
const Path *path2,
|
|
const StaState *sta)
|
|
{
|
|
PathRef p1(path1);
|
|
PathRef p2(path2);
|
|
while (!p1.isNull()
|
|
&& !p2.isNull()) {
|
|
int cmp = Path::cmp(&p1, &p2, sta);
|
|
if (cmp != 0)
|
|
return cmp;
|
|
|
|
TimingArc *prev_arc1, *prev_arc2;
|
|
p1.prevPath(sta, p1, prev_arc1);
|
|
p2.prevPath(sta, p2, prev_arc2);
|
|
if (equal(&p1, path1, sta))
|
|
// Equivalent latch loops.
|
|
return 0;
|
|
if ((prev_arc1 && prev_arc1->role()->isLatchDtoQ())
|
|
|| (prev_arc2 && prev_arc2->role()->isLatchDtoQ()))
|
|
break;
|
|
}
|
|
if (p1.isNull() && p2.isNull())
|
|
return 0;
|
|
else if (p1.isNull() && !p2.isNull())
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
bool
|
|
Path::lessAll(const Path *path1,
|
|
const Path *path2,
|
|
const StaState *sta)
|
|
{
|
|
return cmpAll(path1, path2, sta) < 0;
|
|
}
|
|
|
|
} // namespace
|