2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2024-01-12 01:34:49 +01:00
|
|
|
// Copyright (c) 2024, 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/>.
|
2018-09-28 17:54:21 +02:00
|
|
|
|
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
|
2024-02-27 18:00:48 +01:00
|
|
|
#include <algorithm>
|
2024-04-04 10:20:37 +02:00
|
|
|
#include <stack>
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "Report.hh"
|
|
|
|
|
#include "Debug.hh"
|
2024-04-04 10:20:37 +02:00
|
|
|
#include "DispatchQueue.hh"
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "Units.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
2024-02-27 18:00:48 +01:00
|
|
|
#include "Liberty.hh"
|
2020-04-05 23:53:44 +02:00
|
|
|
#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"
|
2024-05-10 01:56:23 +02:00
|
|
|
#include "PathEnd.hh"
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
using std::abs;
|
|
|
|
|
|
2022-02-20 02:31:52 +01:00
|
|
|
// Source/target clock skew.
|
2018-09-28 17:54:21 +02:00
|
|
|
class ClkSkew
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ClkSkew();
|
|
|
|
|
ClkSkew(PathVertex *src_path,
|
|
|
|
|
PathVertex *tgt_path,
|
2024-05-10 03:06:44 +02:00
|
|
|
bool include_internal_latency,
|
2018-09-28 17:54:21 +02:00
|
|
|
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);
|
2024-05-10 03:06:44 +02:00
|
|
|
float srcInternalClkLatency(StaState *sta);
|
|
|
|
|
float tgtInternalClkLatency(StaState *sta);
|
2018-12-11 19:47:04 +01:00
|
|
|
Crpr crpr(StaState *sta);
|
2024-05-10 01:56:23 +02:00
|
|
|
float uncertainty(StaState *sta);
|
2018-09-28 17:54:21 +02:00
|
|
|
float skew() const { return skew_; }
|
|
|
|
|
|
|
|
|
|
private:
|
2024-02-27 18:00:48 +01:00
|
|
|
float clkTreeDelay(PathVertex &clk_path,
|
|
|
|
|
StaState *sta);
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
PathVertex src_path_;
|
|
|
|
|
PathVertex tgt_path_;
|
2024-05-10 03:06:44 +02:00
|
|
|
bool include_internal_latency_;
|
2018-09-28 17:54:21 +02:00
|
|
|
float skew_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ClkSkew::ClkSkew() :
|
2024-05-21 00:48:00 +02:00
|
|
|
include_internal_latency_(false),
|
2018-09-28 17:54:21 +02:00
|
|
|
skew_(0.0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClkSkew::ClkSkew(PathVertex *src_path,
|
|
|
|
|
PathVertex *tgt_path,
|
2024-05-10 03:06:44 +02:00
|
|
|
bool include_internal_latency,
|
|
|
|
|
StaState *sta) :
|
|
|
|
|
src_path_(src_path),
|
|
|
|
|
tgt_path_(tgt_path),
|
|
|
|
|
include_internal_latency_(include_internal_latency)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2024-05-10 01:56:23 +02:00
|
|
|
skew_ = srcLatency(sta)
|
|
|
|
|
- tgtLatency(sta)
|
|
|
|
|
- delayAsFloat(crpr(sta))
|
|
|
|
|
+ uncertainty(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_;
|
2024-05-10 03:06:44 +02:00
|
|
|
include_internal_latency_ = clk_skew.include_internal_latency_;
|
2019-11-11 21:03:38 +01:00
|
|
|
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_;
|
2024-05-10 03:06:44 +02:00
|
|
|
include_internal_latency_ = clk_skew.include_internal_latency_;
|
2018-09-28 17:54:21 +02:00
|
|
|
skew_ = clk_skew.skew_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
ClkSkew::srcLatency(StaState *sta)
|
|
|
|
|
{
|
|
|
|
|
Arrival src_arrival = src_path_.arrival(sta);
|
2024-02-27 18:00:48 +01:00
|
|
|
return delayAsFloat(src_arrival) - src_path_.clkEdge(sta)->time()
|
|
|
|
|
+ clkTreeDelay(src_path_, sta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
2024-05-10 03:06:44 +02:00
|
|
|
ClkSkew::srcInternalClkLatency(StaState *sta)
|
2024-02-27 18:00:48 +01:00
|
|
|
{
|
|
|
|
|
return clkTreeDelay(src_path_, sta);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
ClkSkew::tgtLatency(StaState *sta)
|
|
|
|
|
{
|
|
|
|
|
Arrival tgt_arrival = tgt_path_.arrival(sta);
|
2024-02-27 18:00:48 +01:00
|
|
|
return delayAsFloat(tgt_arrival) - tgt_path_.clkEdge(sta)->time()
|
|
|
|
|
+ clkTreeDelay(tgt_path_, sta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
2024-05-10 03:06:44 +02:00
|
|
|
ClkSkew::tgtInternalClkLatency(StaState *sta)
|
2024-02-27 18:00:48 +01:00
|
|
|
{
|
|
|
|
|
return clkTreeDelay(tgt_path_, sta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
ClkSkew::clkTreeDelay(PathVertex &clk_path,
|
|
|
|
|
StaState *sta)
|
|
|
|
|
{
|
2024-05-10 03:06:44 +02:00
|
|
|
if (include_internal_latency_) {
|
|
|
|
|
const Vertex *vertex = clk_path.vertex(sta);
|
|
|
|
|
const Pin *pin = vertex->pin();
|
|
|
|
|
const LibertyPort *port = sta->network()->libertyPort(pin);
|
|
|
|
|
const MinMax *min_max = clk_path.minMax(sta);
|
|
|
|
|
const RiseFall *rf = clk_path.transition(sta);
|
|
|
|
|
float slew = delayAsFloat(clk_path.slew(sta));
|
|
|
|
|
return port->clkTreeDelay(slew, rf, min_max);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return 0.0;
|
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
|
|
|
}
|
|
|
|
|
|
2024-05-10 01:56:23 +02:00
|
|
|
float
|
|
|
|
|
ClkSkew::uncertainty(StaState *sta)
|
|
|
|
|
{
|
|
|
|
|
TimingRole *check_role = (src_path_.minMax(sta) == SetupHold::max())
|
|
|
|
|
? TimingRole::setup()
|
|
|
|
|
: TimingRole::hold();
|
|
|
|
|
// Uncertainty decreases slack, but increases skew.
|
|
|
|
|
return -PathEnd::checkTgtClkUncertainty(&tgt_path_, tgt_path_.clkEdge(sta),
|
|
|
|
|
check_role, sta);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
ClkSkews::ClkSkews(StaState *sta) :
|
|
|
|
|
StaState(sta)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2024-05-21 00:48:00 +02:00
|
|
|
ClkSkews::reportClkSkew(ConstClockSeq &clks,
|
2018-11-09 19:04:16 +01:00
|
|
|
const Corner *corner,
|
2018-09-28 17:54:21 +02:00
|
|
|
const SetupHold *setup_hold,
|
2024-05-10 03:06:44 +02:00
|
|
|
bool include_internal_latency,
|
2018-09-28 17:54:21 +02:00
|
|
|
int digits)
|
|
|
|
|
{
|
2024-05-10 03:06:44 +02:00
|
|
|
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold,
|
|
|
|
|
include_internal_latency);
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
// Sort the clocks to report in a stable order.
|
2024-02-27 18:00:48 +01:00
|
|
|
ConstClockSeq sorted_clks;
|
|
|
|
|
for (const Clock *clk : clks)
|
2018-09-28 17:54:21 +02:00
|
|
|
sorted_clks.push_back(clk);
|
2024-02-27 18:00:48 +01:00
|
|
|
std::sort(sorted_clks.begin(), sorted_clks.end(), ClkNameLess());
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2024-02-27 18:00:48 +01:00
|
|
|
for (const Clock *clk : sorted_clks) {
|
2020-12-28 18:04:57 +01:00
|
|
|
report_->reportLine("Clock %s", clk->name());
|
2024-02-27 18:00:48 +01:00
|
|
|
auto skew_itr = skews.find(clk);
|
|
|
|
|
if (skew_itr != skews.end())
|
|
|
|
|
reportClkSkew(skew_itr->second, 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
|
|
|
}
|
2024-02-27 18:00:48 +01:00
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2024-02-27 18:00:48 +01:00
|
|
|
void
|
|
|
|
|
ClkSkews::reportClkSkew(ClkSkew &clk_skew,
|
|
|
|
|
int digits)
|
|
|
|
|
{
|
|
|
|
|
Unit *time_unit = units_->timeUnit();
|
|
|
|
|
PathVertex *src_path = clk_skew.srcPath();
|
|
|
|
|
PathVertex *tgt_path = clk_skew.tgtPath();
|
|
|
|
|
float src_latency = clk_skew.srcLatency(this);
|
|
|
|
|
float tgt_latency = clk_skew.tgtLatency(this);
|
2024-05-10 03:06:44 +02:00
|
|
|
float src_internal_clk_latency = clk_skew.srcInternalClkLatency(this);
|
|
|
|
|
float tgt_internal_clk_latency = clk_skew.tgtInternalClkLatency(this);
|
2024-05-10 01:56:23 +02:00
|
|
|
float uncertainty = clk_skew.uncertainty(this);
|
2024-02-27 18:00:48 +01:00
|
|
|
|
2024-05-10 03:06:44 +02:00
|
|
|
if (src_internal_clk_latency != 0.0)
|
|
|
|
|
src_latency -= src_internal_clk_latency;
|
2024-02-27 18:00:48 +01:00
|
|
|
report_->reportLine("%7s source latency %s %s",
|
|
|
|
|
time_unit->asString(src_latency, digits),
|
|
|
|
|
sdc_network_->pathName(src_path->pin(this)),
|
|
|
|
|
src_path->transition(this)->asString());
|
2024-05-10 03:06:44 +02:00
|
|
|
if (src_internal_clk_latency != 0.0)
|
2024-05-10 01:56:23 +02:00
|
|
|
report_->reportLine("%7s source internal clock delay",
|
2024-05-10 03:06:44 +02:00
|
|
|
time_unit->asString(src_internal_clk_latency, digits));
|
2024-02-27 18:00:48 +01:00
|
|
|
|
2024-05-10 03:06:44 +02:00
|
|
|
if (tgt_internal_clk_latency != 0.0)
|
|
|
|
|
tgt_latency -= tgt_internal_clk_latency;
|
2024-02-27 18:00:48 +01:00
|
|
|
report_->reportLine("%7s target latency %s %s",
|
|
|
|
|
time_unit->asString(-tgt_latency, digits),
|
|
|
|
|
sdc_network_->pathName(tgt_path->pin(this)),
|
|
|
|
|
tgt_path->transition(this)->asString());
|
2024-05-10 03:06:44 +02:00
|
|
|
if (tgt_internal_clk_latency != 0.0)
|
2024-05-10 01:56:23 +02:00
|
|
|
report_->reportLine("%7s target internal clock delay",
|
2024-05-10 03:06:44 +02:00
|
|
|
time_unit->asString(-tgt_internal_clk_latency, digits));
|
2024-05-10 01:56:23 +02:00
|
|
|
if (uncertainty != 0.0)
|
|
|
|
|
report_->reportLine("%7s clock uncertainty",
|
|
|
|
|
time_unit->asString(uncertainty, digits));
|
2024-02-27 18:00:48 +01:00
|
|
|
report_->reportLine("%7s CRPR",
|
|
|
|
|
time_unit->asString(delayAsFloat(-clk_skew.crpr(this)),
|
|
|
|
|
digits));
|
|
|
|
|
report_->reportLine("--------------");
|
|
|
|
|
report_->reportLine("%7s %s skew",
|
|
|
|
|
time_unit->asString(clk_skew.skew(), digits),
|
|
|
|
|
src_path->minMax(this) == MinMax::max() ? "setup" : "hold");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-31 02:22:55 +02:00
|
|
|
float
|
|
|
|
|
ClkSkews::findWorstClkSkew(const Corner *corner,
|
2024-05-10 03:06:44 +02:00
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
bool include_internal_latency)
|
2021-05-31 02:22:55 +02:00
|
|
|
{
|
2024-02-27 18:00:48 +01:00
|
|
|
ConstClockSeq clks;
|
|
|
|
|
for (const Clock *clk : *sdc_->clocks())
|
|
|
|
|
clks.push_back(clk);
|
2024-05-10 03:06:44 +02:00
|
|
|
ClkSkewMap skews = findClkSkew(clks, corner, setup_hold, include_internal_latency);
|
2023-01-19 19:23:45 +01:00
|
|
|
float worst_skew = 0.0;
|
|
|
|
|
for (auto clk_skew_itr : skews) {
|
2024-02-27 18:00:48 +01:00
|
|
|
ClkSkew &clk_skew = clk_skew_itr.second;
|
|
|
|
|
float skew = clk_skew.skew();
|
2023-01-19 19:23:45 +01:00
|
|
|
if (abs(skew) > abs(worst_skew))
|
|
|
|
|
worst_skew = skew;
|
2021-05-31 02:22:55 +02:00
|
|
|
}
|
2023-01-19 19:23:45 +01:00
|
|
|
return worst_skew;
|
2021-05-31 02:22:55 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-27 18:00:48 +01:00
|
|
|
ClkSkewMap
|
|
|
|
|
ClkSkews::findClkSkew(ConstClockSeq &clks,
|
2018-11-09 19:04:16 +01:00
|
|
|
const Corner *corner,
|
2024-05-10 03:06:44 +02:00
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
bool include_internal_latency)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2024-02-27 18:00:48 +01:00
|
|
|
ClkSkewMap skews;
|
|
|
|
|
|
|
|
|
|
ConstClockSet clk_set;
|
|
|
|
|
for (const Clock *clk : clks)
|
|
|
|
|
clk_set.insert(clk);
|
2024-04-04 10:20:37 +02:00
|
|
|
const auto findClkSkew = [this, corner, setup_hold, include_internal_latency, &clk_set](Vertex *src_vertex, ClkSkewMap &skews) {
|
|
|
|
|
VertexOutEdgeIterator edge_iter(src_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (edge->role()->genericRole() != TimingRole::regClkToQ()) continue;
|
|
|
|
|
Vertex *q_vertex = edge->to(graph_);
|
|
|
|
|
RiseFall *rf = edge->timingArcSet()->isRisingFallingEdge();
|
|
|
|
|
RiseFallBoth *src_rf = rf
|
|
|
|
|
? rf->asRiseFallBoth()
|
|
|
|
|
: RiseFallBoth::riseFall();
|
|
|
|
|
findClkSkewFrom(src_vertex, q_vertex, src_rf, clk_set, corner, setup_hold, include_internal_latency, skews);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (threadCount() > 1) {
|
|
|
|
|
std::vector<ClkSkewMap> partial_skews(thread_count_, skews);
|
|
|
|
|
for (Vertex *src_vertex : *graph_->regClkVertices()) {
|
|
|
|
|
if (!hasClkPaths(src_vertex, clk_set)) continue;
|
|
|
|
|
dispatch_queue_->dispatch([findClkSkew, src_vertex, &partial_skews](int i) {
|
|
|
|
|
findClkSkew(src_vertex, partial_skews[i]);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
dispatch_queue_->finishTasks();
|
|
|
|
|
for (size_t i = 0; i < partial_skews.size(); i++) {
|
|
|
|
|
for (auto [clk, partial_skew] : partial_skews[i]) {
|
|
|
|
|
auto ins = skews.insert(std::make_pair(clk, partial_skew));
|
|
|
|
|
if (ins.second) continue;
|
|
|
|
|
ClkSkew &final_skew = ins.first->second;
|
|
|
|
|
if (abs(partial_skew.skew()) > abs(final_skew.skew())) {
|
|
|
|
|
final_skew = partial_skew;
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-04 10:20:37 +02:00
|
|
|
else {
|
|
|
|
|
for (Vertex *src_vertex : *graph_->regClkVertices()) {
|
|
|
|
|
if (!hasClkPaths(src_vertex, clk_set)) continue;
|
|
|
|
|
findClkSkew(src_vertex, skews);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-27 18:00:48 +01:00
|
|
|
return skews;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ClkSkews::hasClkPaths(Vertex *vertex,
|
2024-02-27 18:00:48 +01:00
|
|
|
ConstClockSet &clks)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
VertexPathIterator path_iter(vertex, this);
|
|
|
|
|
while (path_iter.hasNext()) {
|
|
|
|
|
PathVertex *path = path_iter.next();
|
2023-01-19 19:23:45 +01:00
|
|
|
const Clock *path_clk = path->clock(this);
|
2024-02-27 18:00:48 +01:00
|
|
|
if (clks.find(path_clk) != clks.end())
|
2018-09-28 17:54:21 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ClkSkews::findClkSkewFrom(Vertex *src_vertex,
|
|
|
|
|
Vertex *q_vertex,
|
2024-02-27 18:00:48 +01:00
|
|
|
const RiseFallBoth *src_rf,
|
|
|
|
|
ConstClockSet &clk_set,
|
2018-11-09 19:04:16 +01:00
|
|
|
const Corner *corner,
|
2018-09-28 17:54:21 +02:00
|
|
|
const SetupHold *setup_hold,
|
2024-05-10 03:06:44 +02:00
|
|
|
bool include_internal_latency,
|
2018-09-28 17:54:21 +02:00
|
|
|
ClkSkewMap &skews)
|
|
|
|
|
{
|
2022-02-20 02:31:52 +01:00
|
|
|
VertexSet endpoints = findFanout(q_vertex);
|
2024-02-27 18:00:48 +01:00
|
|
|
for (Vertex *end : endpoints) {
|
2018-09-28 17:54:21 +02:00
|
|
|
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_);
|
2024-02-27 18:00:48 +01:00
|
|
|
const RiseFall *tgt_rf1 = edge->timingArcSet()->isRisingFallingEdge();
|
|
|
|
|
const RiseFallBoth *tgt_rf = tgt_rf1
|
2019-11-11 23:30:19 +01:00
|
|
|
? tgt_rf1->asRiseFallBoth()
|
|
|
|
|
: RiseFallBoth::riseFall();
|
|
|
|
|
findClkSkew(src_vertex, src_rf, tgt_vertex, tgt_rf,
|
2024-05-10 03:06:44 +02:00
|
|
|
clk_set, corner, setup_hold,
|
|
|
|
|
include_internal_latency, skews);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ClkSkews::findClkSkew(Vertex *src_vertex,
|
2024-02-27 18:00:48 +01:00
|
|
|
const RiseFallBoth *src_rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
Vertex *tgt_vertex,
|
2024-02-27 18:00:48 +01:00
|
|
|
const RiseFallBoth *tgt_rf,
|
|
|
|
|
ConstClockSet &clk_set,
|
2018-11-09 19:04:16 +01:00
|
|
|
const Corner *corner,
|
2018-09-28 17:54:21 +02:00
|
|
|
const SetupHold *setup_hold,
|
2024-05-10 03:06:44 +02:00
|
|
|
bool include_internal_latency,
|
2024-02-27 18:00:48 +01:00
|
|
|
ClkSkewMap &skews)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
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();
|
2023-01-19 19:23:45 +01:00
|
|
|
const 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
|
2024-02-27 18:00:48 +01:00
|
|
|
&& clk_set.find(src_clk) != clk_set.end()) {
|
2018-11-09 19:04:16 +01:00
|
|
|
Corner *src_corner = src_path->pathAnalysisPt(this)->corner();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (corner == nullptr
|
2018-11-09 19:04:16 +01:00
|
|
|
|| src_corner == corner) {
|
|
|
|
|
VertexPathIterator tgt_iter(tgt_vertex, this);
|
|
|
|
|
while (tgt_iter.hasNext()) {
|
|
|
|
|
PathVertex *tgt_path = tgt_iter.next();
|
2023-01-19 19:23:45 +01:00
|
|
|
const Clock *tgt_clk = tgt_path->clock(this);
|
2018-11-09 19:04:16 +01:00
|
|
|
if (tgt_clk == src_clk
|
2020-09-23 02:29:10 +02:00
|
|
|
&& tgt_path->isClock(this)
|
2019-11-11 23:30:19 +01:00
|
|
|
&& tgt_rf->matches(tgt_path->transition(this))
|
2018-11-09 19:04:16 +01:00
|
|
|
&& tgt_path->minMax(this) == tgt_min_max
|
|
|
|
|
&& tgt_path->pathAnalysisPt(this)->corner() == src_corner) {
|
2024-05-10 03:06:44 +02:00
|
|
|
ClkSkew probe(src_path, tgt_path, include_internal_latency, this);
|
2024-02-27 18:00:48 +01:00
|
|
|
ClkSkew &clk_skew = skews[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()));
|
2024-02-27 18:00:48 +01:00
|
|
|
if (clk_skew.srcPath()->isNull()
|
|
|
|
|
|| abs(probe.skew()) > abs(clk_skew.skew()))
|
|
|
|
|
clk_skew = probe;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-01 00:51:09 +01:00
|
|
|
class FanOutSrchPred : public SearchPred1
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FanOutSrchPred(const StaState *sta);
|
|
|
|
|
virtual bool searchThru(Edge *edge);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FanOutSrchPred::FanOutSrchPred(const StaState *sta) :
|
2022-03-01 00:51:09 +01:00
|
|
|
SearchPred1(sta)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
FanOutSrchPred::searchThru(Edge *edge)
|
|
|
|
|
{
|
|
|
|
|
TimingRole *role = edge->role();
|
2022-03-01 00:51:09 +01:00
|
|
|
return SearchPred1::searchThru(edge)
|
|
|
|
|
&& (role == TimingRole::wire()
|
|
|
|
|
|| role == TimingRole::combinational()
|
|
|
|
|
|| role == TimingRole::tristateEnable()
|
|
|
|
|
|| role == TimingRole::tristateDisable());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-20 02:31:52 +01:00
|
|
|
VertexSet
|
|
|
|
|
ClkSkews::findFanout(Vertex *from)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2021-01-01 20:46:51 +01:00
|
|
|
debugPrint(debug_, "fanout", 1, "%s",
|
|
|
|
|
from->name(sdc_network_));
|
2022-03-01 14:55:25 +01:00
|
|
|
VertexSet endpoints(graph_);
|
2018-09-28 17:54:21 +02:00
|
|
|
FanOutSrchPred pred(this);
|
2024-04-04 10:20:37 +02:00
|
|
|
if (threadCount() > 1) {
|
|
|
|
|
// This is called from multiple threads, so BfsVisitor cannot be used, as it modifies vertex state.
|
|
|
|
|
// Uses depth-first search; breadth-first would work too, but a stack is slightly faster than a queue.
|
|
|
|
|
thread_local static std::unordered_set<Vertex *> visited;
|
|
|
|
|
thread_local static std::stack<Vertex *, std::vector<Vertex *>> stack;
|
|
|
|
|
visited.clear();
|
|
|
|
|
stack.push(from);
|
|
|
|
|
visited.insert(from);
|
|
|
|
|
while (!stack.empty()) {
|
|
|
|
|
Vertex *fanout = stack.top();
|
|
|
|
|
stack.pop();
|
|
|
|
|
if (fanout->hasChecks()) {
|
|
|
|
|
debugPrint(debug_, "fanout", 1, " endpoint %s",
|
|
|
|
|
fanout->name(sdc_network_));
|
|
|
|
|
endpoints.insert(fanout);
|
|
|
|
|
}
|
|
|
|
|
if (!pred.searchFrom(fanout)) continue;
|
|
|
|
|
VertexOutEdgeIterator edge_iter(fanout, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
Vertex *to = edge->to(graph_);
|
|
|
|
|
if (pred.searchThru(edge) && pred.searchTo(to) && visited.insert(to).second) {
|
|
|
|
|
stack.push(to);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BfsFwdIterator fanout_iter(BfsIndex::other, &pred, this);
|
|
|
|
|
fanout_iter.enqueue(from);
|
|
|
|
|
while (fanout_iter.hasNext()) {
|
|
|
|
|
Vertex *fanout = fanout_iter.next();
|
|
|
|
|
if (fanout->hasChecks()) {
|
|
|
|
|
debugPrint(debug_, "fanout", 1, " endpoint %s",
|
|
|
|
|
fanout->name(sdc_network_));
|
|
|
|
|
endpoints.insert(fanout);
|
|
|
|
|
}
|
|
|
|
|
fanout_iter.enqueueAdjacentVertices(fanout);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-02-20 02:31:52 +01:00
|
|
|
return endpoints;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|