318 lines
11 KiB
C++
318 lines
11 KiB
C++
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2025, 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/>.
|
|
//
|
|
// The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software.
|
|
//
|
|
// Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
//
|
|
// This notice may not be removed or altered from any source distribution.
|
|
|
|
#include "ClkLatency.hh"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "Report.hh"
|
|
#include "Debug.hh"
|
|
#include "Units.hh"
|
|
#include "Liberty.hh"
|
|
#include "Network.hh"
|
|
#include "Clock.hh"
|
|
#include "Graph.hh"
|
|
#include "Path.hh"
|
|
#include "StaState.hh"
|
|
#include "Search.hh"
|
|
#include "PathAnalysisPt.hh"
|
|
#include "ClkInfo.hh"
|
|
|
|
namespace sta {
|
|
|
|
ClkLatency::ClkLatency(StaState *sta) :
|
|
StaState(sta)
|
|
{
|
|
}
|
|
|
|
ClkDelays
|
|
ClkLatency::findClkDelays(const Clock *clk,
|
|
const Corner *corner,
|
|
bool include_internal_latency)
|
|
{
|
|
ConstClockSeq clks;
|
|
clks.push_back(clk);
|
|
ClkDelayMap clk_delay_map = findClkDelays(clks, corner,
|
|
include_internal_latency);
|
|
return clk_delay_map[clk];
|
|
}
|
|
|
|
void
|
|
ClkLatency::reportClkLatency(ConstClockSeq &clks,
|
|
const Corner *corner,
|
|
bool include_internal_latency,
|
|
int digits)
|
|
{
|
|
ClkDelayMap clk_delay_map = findClkDelays(clks, corner, include_internal_latency);
|
|
|
|
// Sort the clocks to report in a stable order.
|
|
ConstClockSeq sorted_clks;
|
|
for (const Clock *clk : clks)
|
|
sorted_clks.push_back(clk);
|
|
std::sort(sorted_clks.begin(), sorted_clks.end(), ClkNameLess());
|
|
|
|
for (const Clock *clk : sorted_clks) {
|
|
ClkDelays clk_delays = clk_delay_map[clk];
|
|
reportClkLatency(clk, clk_delays, digits);
|
|
report_->reportBlankLine();
|
|
}
|
|
}
|
|
|
|
void
|
|
ClkLatency::reportClkLatency(const Clock *clk,
|
|
ClkDelays &clk_delays,
|
|
int digits)
|
|
{
|
|
Unit *time_unit = units_->timeUnit();
|
|
report_->reportLine("Clock %s", clk->name());
|
|
for (const RiseFall *src_rf : RiseFall::range()) {
|
|
for (const RiseFall *end_rf : RiseFall::range()) {
|
|
Path path_min;
|
|
Delay insertion_min;
|
|
Delay delay_min;
|
|
float internal_latency_min;
|
|
Delay latency_min;
|
|
bool exists_min;
|
|
clk_delays.delay(src_rf, end_rf, MinMax::min(), insertion_min,
|
|
delay_min, internal_latency_min, latency_min,
|
|
path_min, exists_min);
|
|
Path path_max;
|
|
Delay insertion_max;
|
|
Delay delay_max;
|
|
float internal_latency_max;
|
|
Delay latency_max;
|
|
bool exists_max;
|
|
clk_delays.delay(src_rf, end_rf, MinMax::max(), insertion_max,
|
|
delay_max, internal_latency_max, latency_max,
|
|
path_max, exists_max);
|
|
if (exists_min & exists_max) {
|
|
report_->reportLine("%s -> %s",
|
|
src_rf->name(),
|
|
end_rf->name());
|
|
report_->reportLine(" min max");
|
|
|
|
report_->reportLine("%7s %7s source latency",
|
|
delayAsString(insertion_min, this, digits),
|
|
delayAsString(insertion_max, this, digits));
|
|
report_->reportLine("%7s %7s network latency %s",
|
|
delayAsString(delay_min, this, digits),
|
|
"",
|
|
sdc_network_->pathName(path_min.pin(this)));
|
|
report_->reportLine("%7s %7s network latency %s",
|
|
"",
|
|
delayAsString(delay_max, this, digits),
|
|
sdc_network_->pathName(path_max.pin(this)));
|
|
if (internal_latency_min != 0.0
|
|
|| internal_latency_max != 0.0)
|
|
report_->reportLine("%7s %7s internal clock latency",
|
|
time_unit->asString(internal_latency_min, digits),
|
|
time_unit->asString(internal_latency_max, digits));
|
|
report_->reportLine("---------------");
|
|
report_->reportLine("%7s %7s latency",
|
|
delayAsString(latency_min, this, digits),
|
|
delayAsString(latency_max, this, digits));
|
|
Delay skew = latency_max - latency_min;
|
|
report_->reportLine(" %7s skew",
|
|
delayAsString(skew, this, digits));
|
|
report_->reportBlankLine();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ClkDelayMap
|
|
ClkLatency::findClkDelays(ConstClockSeq &clks,
|
|
const Corner *corner,
|
|
bool include_internal_latency)
|
|
{
|
|
ClkDelayMap clk_delay_map;
|
|
// Make entries for the relevant clocks to filter path clocks.
|
|
for (const Clock *clk : clks)
|
|
clk_delay_map[clk];
|
|
for (Vertex *clk_vertex : *graph_->regClkVertices()) {
|
|
VertexPathIterator path_iter(clk_vertex, this);
|
|
while (path_iter.hasNext()) {
|
|
Path *path = path_iter.next();
|
|
const ClockEdge *path_clk_edge = path->clkEdge(this);
|
|
const PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
|
|
if (path_clk_edge
|
|
&& (corner == nullptr
|
|
|| path_ap->corner() == corner)) {
|
|
const Clock *path_clk = path_clk_edge->clock();
|
|
auto delays_itr = clk_delay_map.find(path_clk);
|
|
if (delays_itr != clk_delay_map.end()) {
|
|
ClkDelays &clk_delays = delays_itr->second;
|
|
const RiseFall *clk_rf = path_clk_edge->transition();
|
|
const MinMax *min_max = path->minMax(this);
|
|
const RiseFall *end_rf = path->transition(this);
|
|
Delay latency = ClkDelays::latency(path, this);
|
|
Delay clk_latency;
|
|
bool exists;
|
|
clk_delays.latency(clk_rf, end_rf, min_max, clk_latency, exists);
|
|
if (!exists || delayGreater(latency, clk_latency, min_max, this))
|
|
clk_delays.setLatency(clk_rf, end_rf, min_max, path,
|
|
include_internal_latency, this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return clk_delay_map;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
ClkDelays::ClkDelays()
|
|
{
|
|
for (auto src_rf_index : RiseFall::rangeIndex()) {
|
|
for (auto end_rf_index : RiseFall::rangeIndex()) {
|
|
for (auto mm_index : MinMax::rangeIndex()) {
|
|
insertion_[src_rf_index][end_rf_index][mm_index] = 0.0;
|
|
delay_[src_rf_index][end_rf_index][mm_index] = 0.0;
|
|
internal_latency_[src_rf_index][end_rf_index][mm_index] = 0.0;
|
|
latency_[src_rf_index][end_rf_index][mm_index] = 0.0;
|
|
exists_[src_rf_index][end_rf_index][mm_index] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ClkDelays::delay(const RiseFall *src_rf,
|
|
const RiseFall *end_rf,
|
|
const MinMax *min_max,
|
|
// Return values.
|
|
Delay &insertion,
|
|
Delay &delay,
|
|
float &lib_clk_delay,
|
|
Delay &latency,
|
|
Path &path,
|
|
bool &exists) const
|
|
{
|
|
int src_rf_index = src_rf->index();
|
|
int end_rf_index = end_rf->index();
|
|
int mm_index = min_max->index();
|
|
path = path_[src_rf_index][end_rf_index][mm_index];
|
|
insertion = insertion_[src_rf_index][end_rf_index][mm_index];
|
|
delay = delay_[src_rf_index][end_rf_index][mm_index];
|
|
lib_clk_delay = internal_latency_[src_rf_index][end_rf_index][mm_index];
|
|
latency = latency_[src_rf_index][end_rf_index][mm_index];
|
|
exists = exists_[src_rf_index][end_rf_index][mm_index];
|
|
}
|
|
|
|
void
|
|
ClkDelays::latency(const RiseFall *src_rf,
|
|
const RiseFall *end_rf,
|
|
const MinMax *min_max,
|
|
// Return values.
|
|
Delay &latency,
|
|
bool &exists) const
|
|
{
|
|
int src_rf_index = src_rf->index();
|
|
int end_rf_index = end_rf->index();
|
|
int mm_index = min_max->index();
|
|
latency = latency_[src_rf_index][end_rf_index][mm_index];
|
|
exists = exists_[src_rf_index][end_rf_index][mm_index];
|
|
}
|
|
|
|
void
|
|
ClkDelays::setLatency(const RiseFall *src_rf,
|
|
const RiseFall *end_rf,
|
|
const MinMax *min_max,
|
|
Path *path,
|
|
bool include_internal_latency,
|
|
StaState *sta)
|
|
{
|
|
int src_rf_index = src_rf->index();
|
|
int end_rf_index = end_rf->index();
|
|
int mm_index = min_max->index();
|
|
|
|
float insertion = insertionDelay(path, sta);
|
|
insertion_[src_rf_index][end_rf_index][mm_index] = insertion;
|
|
|
|
float delay1 = delay(path, sta);
|
|
delay_[src_rf_index][end_rf_index][mm_index] = delay1;
|
|
|
|
float internal_latency = 0.0;
|
|
if (include_internal_latency) {
|
|
internal_latency = clkTreeDelay(path, sta);
|
|
internal_latency_[src_rf_index][end_rf_index][mm_index] = internal_latency;
|
|
}
|
|
|
|
float latency = insertion + delay1 + internal_latency;
|
|
latency_[src_rf_index][end_rf_index][mm_index] = latency;
|
|
|
|
path_[src_rf_index][end_rf_index][mm_index] = *path;
|
|
exists_[src_rf_index][end_rf_index][mm_index] = true;
|
|
}
|
|
|
|
Delay
|
|
ClkDelays::latency(Path *clk_path,
|
|
StaState *sta)
|
|
{
|
|
|
|
float insertion = insertionDelay(clk_path, sta);
|
|
float delay1 = delay(clk_path, sta);
|
|
float lib_clk_delay = clkTreeDelay(clk_path, sta);
|
|
return insertion + delay1 + lib_clk_delay;
|
|
}
|
|
|
|
float
|
|
ClkDelays::delay(Path *clk_path,
|
|
StaState *sta)
|
|
{
|
|
Arrival arrival = clk_path->arrival();
|
|
const ClockEdge *path_clk_edge = clk_path->clkEdge(sta);
|
|
return delayAsFloat(arrival) - path_clk_edge->time();
|
|
}
|
|
|
|
float
|
|
ClkDelays::insertionDelay(Path *clk_path,
|
|
StaState *sta)
|
|
{
|
|
const ClockEdge *clk_edge = clk_path->clkEdge(sta);
|
|
const Clock *clk = clk_edge->clock();
|
|
const RiseFall *clk_rf = clk_edge->transition();
|
|
const ClkInfo *clk_info = clk_path->clkInfo(sta);
|
|
const Pin *src_pin = clk_info->clkSrc();
|
|
const PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(sta);
|
|
const MinMax *min_max = clk_path->minMax(sta);
|
|
return delayAsFloat(sta->search()->clockInsertion(clk, src_pin, clk_rf, min_max,
|
|
min_max, path_ap));
|
|
}
|
|
|
|
float
|
|
ClkDelays::clkTreeDelay(Path *clk_path,
|
|
StaState *sta)
|
|
{
|
|
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);
|
|
}
|
|
|
|
} // namespace
|