OpenSTA/search/ClkSkew.cc

343 lines
9.3 KiB
C++
Raw Normal View History

2018-09-28 17:54:21 +02:00
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2018, 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 <cmath> // abs
#include "Machine.hh"
#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"
#include "ClkSkew.hh"
namespace sta {
using std::abs;
class ClkSkew
{
public:
ClkSkew();
ClkSkew(PathVertex *src_path,
PathVertex *tgt_path,
StaState *sta);
ClkSkew(ClkSkew &clk_skew);
void copy(ClkSkew &clk_skew);
PathVertex *srcPath() { return &src_path_; }
PathVertex *tgtPath() { return &tgt_path_; }
float srcLatency(StaState *sta);
float tgtLatency(StaState *sta);
float crpr(StaState *sta);
float skew() const { return skew_; }
private:
PathVertex src_path_;
PathVertex tgt_path_;
float skew_;
DISALLOW_COPY_AND_ASSIGN(ClkSkew);
};
ClkSkew::ClkSkew() :
skew_(0.0)
{
}
ClkSkew::ClkSkew(PathVertex *src_path,
PathVertex *tgt_path,
StaState *sta)
{
src_path_.copy(src_path);
tgt_path_.copy(tgt_path);
skew_ = srcLatency(sta) - tgtLatency(sta) - crpr(sta);
}
ClkSkew::ClkSkew(ClkSkew &clk_skew)
{
copy(clk_skew);
}
void
ClkSkew::copy(ClkSkew &clk_skew)
{
src_path_.copy(clk_skew.src_path_);
tgt_path_.copy(clk_skew.tgt_path_);
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
}
float
ClkSkew::crpr(StaState *sta)
{
Crpr *crpr = sta->search()->crpr();
return crpr->checkCrpr(&src_path_, &tgt_path_);
}
////////////////////////////////////////////////////////////////
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;
ClockSet::Iterator clk_iter1(clks);
while (clk_iter1.hasNext()) {
Clock *clk = clk_iter1.next();
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();
report_->print("Clock %s\n", clk->name());
ClkSkew *clk_skew = skews.findKey(clk);
if (clk_skew) {
report_->print("Latency CRPR Skew\n");
PathVertex *src_path = clk_skew->srcPath();
PathVertex *tgt_path = clk_skew->tgtPath();
report_->print("%s %s\n",
sdc_network_->pathName(src_path->pin(this)),
src_path->transition(this)->asString());
report_->print("%7s\n",
time_unit->asString(clk_skew->srcLatency(this), digits));
report_->print("%s %s\n",
sdc_network_->pathName(tgt_path->pin(this)),
tgt_path->transition(this)->asString());
report_->print("%7s %7s %7s\n",
time_unit->asString(clk_skew->tgtLatency(this), digits),
time_unit->asString(-clk_skew->crpr(this), digits),
time_unit->asString(clk_skew->skew(), digits));
}
else
report_->print("No launch/capture paths found.\n");
report_->print("\n");
}
skews.deleteContents();
}
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_);
TransRiseFall *tr = edge->timingArcSet()->isRisingFallingEdge();
TransRiseFallBoth *src_tr = tr
? tr->asRiseFallBoth()
: TransRiseFallBoth::riseFall();
findClkSkewFrom(src_vertex, q_vertex, src_tr, 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,
TransRiseFallBoth *src_tr,
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_);
TransRiseFall *tgt_tr1 = edge->timingArcSet()->isRisingFallingEdge();
TransRiseFallBoth *tgt_tr = tgt_tr1
? tgt_tr1->asRiseFallBoth()
: TransRiseFallBoth::riseFall();
findClkSkew(src_vertex, src_tr, tgt_vertex, tgt_tr,
clks, corner, setup_hold, skews);
2018-09-28 17:54:21 +02:00
}
}
}
}
void
ClkSkews::findClkSkew(Vertex *src_vertex,
TransRiseFallBoth *src_tr,
Vertex *tgt_vertex,
TransRiseFallBoth *tgt_tr,
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);
if (src_tr->matches(src_path->transition(this))
&& src_path->minMax(this) == setup_hold
&& clks->hasKey(src_clk)) {
Corner *src_corner = src_path->pathAnalysisPt(this)->corner();
if (corner == NULL
|| 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_tr->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);
debugPrint8(debug_, "clk_skew", 2, "%s %s %s -> %s %s %s crpr = %s skew = %s\n",
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)),
time_unit->asString(probe.crpr(this)),
time_unit->asString(probe.skew()));
if (clk_skew == NULL) {
clk_skew = new ClkSkew(probe);
skews[src_clk] = clk_skew;
}
else if (fuzzyGreater(probe.skew(), clk_skew->skew()))
clk_skew->copy(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)
{
debugPrint1(debug_, "fanout", 1, "%s\n",
from->name(sdc_network_));
FanOutSrchPred pred(this);
BfsFwdIterator fanout_iter(bfs_other, &pred, this);
fanout_iter.enqueue(from);
while (fanout_iter.hasNext()) {
Vertex *fanout = fanout_iter.next();
if (fanout->hasChecks()) {
debugPrint1(debug_, "fanout", 1, " endpoint %s\n",
fanout->name(sdc_network_));
endpoints.insert(fanout);
}
fanout_iter.enqueueAdjacentVertices(fanout);
}
}
} // namespace