OpenSTA/search/ClkSkew.cc

363 lines
10 KiB
C++
Raw Normal View History

2018-09-28 17:54:21 +02:00
// OpenSTA, Static Timing Analyzer
2021-06-25 19:25:49 +02:00
// Copyright (c) 2021, 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/>.
2020-04-05 23:53:44 +02:00
#include "ClkSkew.hh"
2020-04-05 20:35:51 +02:00
2018-09-28 17:54:21 +02:00
#include <cmath> // abs
2020-04-05 20:35:51 +02:00
2020-04-05 23:53:44 +02:00
#include "DisallowCopyAssign.hh"
#include "Report.hh"
#include "Debug.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "TimingArc.hh"
#include "Network.hh"
#include "Graph.hh"
#include "Sdc.hh"
#include "Bfs.hh"
#include "PathVertex.hh"
#include "StaState.hh"
#include "PathAnalysisPt.hh"
#include "SearchPred.hh"
#include "Search.hh"
#include "Crpr.hh"
2018-09-28 17:54:21 +02:00
namespace sta {
using std::abs;
class ClkSkew
{
public:
ClkSkew();
ClkSkew(PathVertex *src_path,
PathVertex *tgt_path,
StaState *sta);
2019-11-11 21:03:38 +01:00
ClkSkew(const ClkSkew &clk_skew);
void operator=(const ClkSkew &clk_skew);
2018-09-28 17:54:21 +02:00
PathVertex *srcPath() { return &src_path_; }
PathVertex *tgtPath() { return &tgt_path_; }
float srcLatency(StaState *sta);
float tgtLatency(StaState *sta);
2018-12-11 19:47:04 +01:00
Crpr crpr(StaState *sta);
2018-09-28 17:54:21 +02:00
float skew() const { return skew_; }
private:
PathVertex src_path_;
PathVertex tgt_path_;
float skew_;
};
ClkSkew::ClkSkew() :
skew_(0.0)
{
}
ClkSkew::ClkSkew(PathVertex *src_path,
PathVertex *tgt_path,
StaState *sta)
{
2019-11-11 21:03:38 +01:00
src_path_ = src_path;
tgt_path_ = tgt_path;
2018-12-11 19:47:04 +01:00
skew_ = srcLatency(sta) - tgtLatency(sta) - delayAsFloat(crpr(sta));
2018-09-28 17:54:21 +02:00
}
2019-11-11 21:03:38 +01:00
ClkSkew::ClkSkew(const ClkSkew &clk_skew)
2018-09-28 17:54:21 +02:00
{
2019-11-11 21:03:38 +01:00
src_path_ = clk_skew.src_path_;
tgt_path_ = clk_skew.tgt_path_;
skew_ = clk_skew.skew_;
2018-09-28 17:54:21 +02:00
}
void
2019-11-11 21:03:38 +01:00
ClkSkew::operator=(const ClkSkew &clk_skew)
2018-09-28 17:54:21 +02:00
{
2019-11-11 21:03:38 +01:00
src_path_ = clk_skew.src_path_;
tgt_path_ = clk_skew.tgt_path_;
2018-09-28 17:54:21 +02:00
skew_ = clk_skew.skew_;
}
float
ClkSkew::srcLatency(StaState *sta)
{
Arrival src_arrival = src_path_.arrival(sta);
2018-11-26 18:15:52 +01:00
return delayAsFloat(src_arrival) - src_path_.clkEdge(sta)->time();
2018-09-28 17:54:21 +02:00
}
float
ClkSkew::tgtLatency(StaState *sta)
{
Arrival tgt_arrival = tgt_path_.arrival(sta);
2018-11-26 18:15:52 +01:00
return delayAsFloat(tgt_arrival) - tgt_path_.clkEdge(sta)->time();
2018-09-28 17:54:21 +02:00
}
2018-12-11 19:47:04 +01:00
Crpr
2018-09-28 17:54:21 +02:00
ClkSkew::crpr(StaState *sta)
{
2018-12-11 19:47:04 +01:00
CheckCrpr *check_crpr = sta->search()->checkCrpr();
return check_crpr->checkCrpr(&src_path_, &tgt_path_);
2018-09-28 17:54:21 +02:00
}
////////////////////////////////////////////////////////////////
ClkSkews::ClkSkews(StaState *sta) :
StaState(sta)
{
}
void
ClkSkews::reportClkSkew(ClockSet *clks,
const Corner *corner,
2018-09-28 17:54:21 +02:00
const SetupHold *setup_hold,
int digits)
{
ClkSkewMap skews;
findClkSkew(clks, corner, setup_hold, skews);
2018-09-28 17:54:21 +02:00
// Sort the clocks to report in a stable order.
ClockSeq sorted_clks;
2021-05-31 02:22:55 +02:00
for (Clock *clk : *clks)
2018-09-28 17:54:21 +02:00
sorted_clks.push_back(clk);
sort(sorted_clks, ClkNameLess());
Unit *time_unit = units_->timeUnit();
ClockSeq::Iterator clk_iter2(sorted_clks);
while (clk_iter2.hasNext()) {
Clock *clk = clk_iter2.next();
2020-12-28 18:04:57 +01:00
report_->reportLine("Clock %s", clk->name());
2018-09-28 17:54:21 +02:00
ClkSkew *clk_skew = skews.findKey(clk);
if (clk_skew) {
2020-12-28 18:04:57 +01:00
report_->reportLine("Latency CRPR Skew");
2018-09-28 17:54:21 +02:00
PathVertex *src_path = clk_skew->srcPath();
PathVertex *tgt_path = clk_skew->tgtPath();
2020-12-28 18:04:57 +01:00
report_->reportLine("%s %s",
sdc_network_->pathName(src_path->pin(this)),
src_path->transition(this)->asString());
report_->reportLine("%7s",
time_unit->asString(clk_skew->srcLatency(this), digits));
report_->reportLine("%s %s",
sdc_network_->pathName(tgt_path->pin(this)),
tgt_path->transition(this)->asString());
report_->reportLine("%7s %7s %7s",
time_unit->asString(clk_skew->tgtLatency(this), digits),
time_unit->asString(delayAsFloat(-clk_skew->crpr(this)),
digits),
time_unit->asString(clk_skew->skew(), digits));
2018-09-28 17:54:21 +02:00
}
else
2020-12-28 18:04:57 +01:00
report_->reportLine("No launch/capture paths found.");
2021-01-05 03:14:04 +01:00
report_->reportBlankLine();
2018-09-28 17:54:21 +02:00
}
skews.deleteContents();
}
2021-05-31 02:22:55 +02:00
float
ClkSkews::findWorstClkSkew(const Corner *corner,
const SetupHold *setup_hold)
{
ClockSet clks;
for (Clock *clk : *sdc_->clocks())
clks.insert(clk);
float worst_skew = INF;
ClkSkewMap skews;
findClkSkew(&clks, corner, setup_hold, skews);
for (auto clk_skew_itr : skews) {
ClkSkew *clk_skew = clk_skew_itr.second;
float skew = clk_skew->skew();
if (skew < worst_skew)
worst_skew = skew;
}
return worst_skew;
}
2018-09-28 17:54:21 +02:00
void
ClkSkews::findClkSkew(ClockSet *clks,
const Corner *corner,
2018-09-28 17:54:21 +02:00
const SetupHold *setup_hold,
ClkSkewMap &skews)
{
VertexSet::ConstIterator reg_clk_iter(graph_->regClkVertices());
while (reg_clk_iter.hasNext()) {
Vertex *src_vertex = reg_clk_iter.next();
if (hasClkPaths(src_vertex, clks)) {
VertexOutEdgeIterator edge_iter(src_vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role()->genericRole() == TimingRole::regClkToQ()) {
Vertex *q_vertex = edge->to(graph_);
2019-11-11 23:30:19 +01:00
RiseFall *rf = edge->timingArcSet()->isRisingFallingEdge();
RiseFallBoth *src_rf = rf
? rf->asRiseFallBoth()
: RiseFallBoth::riseFall();
findClkSkewFrom(src_vertex, q_vertex, src_rf, clks,
corner, setup_hold, skews);
2018-09-28 17:54:21 +02:00
}
}
}
}
}
bool
ClkSkews::hasClkPaths(Vertex *vertex,
ClockSet *clks)
{
VertexPathIterator path_iter(vertex, this);
while (path_iter.hasNext()) {
PathVertex *path = path_iter.next();
Clock *path_clk = path->clock(this);
if (clks->hasKey(path_clk))
return true;
}
return false;
}
void
ClkSkews::findClkSkewFrom(Vertex *src_vertex,
Vertex *q_vertex,
2019-11-11 23:30:19 +01:00
RiseFallBoth *src_rf,
2018-09-28 17:54:21 +02:00
ClockSet *clks,
const Corner *corner,
2018-09-28 17:54:21 +02:00
const SetupHold *setup_hold,
ClkSkewMap &skews)
{
VertexSet endpoints;
findFanout(q_vertex, endpoints);
VertexSet::Iterator end_iter(endpoints);
while (end_iter.hasNext()) {
Vertex *end = end_iter.next();
VertexInEdgeIterator edge_iter(end, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
TimingRole *role = edge->role();
if (role->isTimingCheck()
&& ((setup_hold == SetupHold::max()
&& role->genericRole() == TimingRole::setup())
|| ((setup_hold == SetupHold::min()
&& role->genericRole() == TimingRole::hold())))) {
Vertex *tgt_vertex = edge->from(graph_);
2019-11-11 23:30:19 +01:00
RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge();
RiseFallBoth *tgt_rf = tgt_rf1
? tgt_rf1->asRiseFallBoth()
: RiseFallBoth::riseFall();
findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf,
clks, corner, setup_hold, skews);
2018-09-28 17:54:21 +02:00
}
}
}
}
void
ClkSkews::findClkSkew(Vertex *src_vertex,
2019-11-11 23:30:19 +01:00
RiseFallBoth *src_rf,
2018-09-28 17:54:21 +02:00
Vertex *tgt_vertex,
2019-11-11 23:30:19 +01:00
RiseFallBoth *tgt_rf,
2018-09-28 17:54:21 +02:00
ClockSet *clks,
const Corner *corner,
2018-09-28 17:54:21 +02:00
const SetupHold *setup_hold,
ClkSkewMap &skews)
{
Unit *time_unit = units_->timeUnit();
const SetupHold *tgt_min_max = setup_hold->opposite();
VertexPathIterator src_iter(src_vertex, this);
while (src_iter.hasNext()) {
PathVertex *src_path = src_iter.next();
Clock *src_clk = src_path->clock(this);
2019-11-11 23:30:19 +01:00
if (src_rf->matches(src_path->transition(this))
2018-09-28 17:54:21 +02:00
&& src_path->minMax(this) == setup_hold
&& clks->hasKey(src_clk)) {
Corner *src_corner = src_path->pathAnalysisPt(this)->corner();
2019-03-13 01:25:53 +01:00
if (corner == nullptr
|| src_corner == corner) {
VertexPathIterator tgt_iter(tgt_vertex, this);
while (tgt_iter.hasNext()) {
PathVertex *tgt_path = tgt_iter.next();
Clock *tgt_clk = tgt_path->clock(this);
if (tgt_clk == src_clk
&& tgt_path->isClock(this)
2019-11-11 23:30:19 +01:00
&& tgt_rf->matches(tgt_path->transition(this))
&& tgt_path->minMax(this) == tgt_min_max
&& tgt_path->pathAnalysisPt(this)->corner() == src_corner) {
ClkSkew probe(src_path, tgt_path, this);
ClkSkew *clk_skew = skews.findKey(src_clk);
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "clk_skew", 2,
"%s %s %s -> %s %s %s crpr = %s skew = %s",
network_->pathName(src_path->pin(this)),
src_path->transition(this)->asString(),
time_unit->asString(probe.srcLatency(this)),
network_->pathName(tgt_path->pin(this)),
tgt_path->transition(this)->asString(),
time_unit->asString(probe.tgtLatency(this)),
delayAsString(probe.crpr(this), this),
time_unit->asString(probe.skew()));
2019-03-13 01:25:53 +01:00
if (clk_skew == nullptr) {
clk_skew = new ClkSkew(probe);
skews[src_clk] = clk_skew;
}
else if (fuzzyGreater(probe.skew(), clk_skew->skew()))
2019-11-11 21:03:38 +01:00
*clk_skew = probe;
2018-09-28 17:54:21 +02:00
}
}
}
}
}
}
class FanOutSrchPred : public SearchPred0
{
public:
FanOutSrchPred(const StaState *sta);
virtual bool searchThru(Edge *edge);
};
FanOutSrchPred::FanOutSrchPred(const StaState *sta) :
SearchPred0(sta)
{
}
bool
FanOutSrchPred::searchThru(Edge *edge)
{
TimingRole *role = edge->role();
return role == TimingRole::wire()
|| role == TimingRole::combinational()
|| role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable();
}
void
ClkSkews::findFanout(Vertex *from,
// Return value.
VertexSet &endpoints)
{
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "fanout", 1, "%s",
from->name(sdc_network_));
2018-09-28 17:54:21 +02:00
FanOutSrchPred pred(this);
2019-03-13 01:25:53 +01:00
BfsFwdIterator fanout_iter(BfsIndex::other, &pred, this);
2018-09-28 17:54:21 +02:00
fanout_iter.enqueue(from);
while (fanout_iter.hasNext()) {
Vertex *fanout = fanout_iter.next();
if (fanout->hasChecks()) {
2021-01-01 20:46:51 +01:00
debugPrint(debug_, "fanout", 1, " endpoint %s",
fanout->name(sdc_network_));
2018-09-28 17:54:21 +02:00
endpoints.insert(fanout);
}
fanout_iter.enqueueAdjacentVertices(fanout);
}
}
} // namespace