// 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 .
//
// 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 "PathEnd.hh"
#include "Debug.hh"
#include "TimingRole.hh"
#include "TimingArc.hh"
#include "Liberty.hh"
#include "Network.hh"
#include "Graph.hh"
#include "Clock.hh"
#include "PortDelay.hh"
#include "DataCheck.hh"
#include "Sdc.hh"
#include "ExceptionPath.hh"
#include "ClkInfo.hh"
#include "Tag.hh"
#include "PathAnalysisPt.hh"
#include "Search.hh"
#include "ReportPath.hh"
#include "Sim.hh"
#include "Latches.hh"
#include "StaState.hh"
#include "PathExpanded.hh"
#include "search/Crpr.hh"
namespace sta {
PathEnd::PathEnd(Path *path) :
path_(path),
path_group_(nullptr)
{
}
PathEnd::~PathEnd()
{
if (path_->isEnum())
delete path_;
}
void
PathEnd::setPath(Path *path)
{
path_ = path;
}
void
PathEnd::setPathGroup(PathGroup *path_group)
{
path_group_ = path_group;
}
Vertex *
PathEnd::vertex(const StaState *sta) const
{
return path_->vertex(sta);
}
const MinMax *
PathEnd::minMax(const StaState *sta) const
{
return path_->pathAnalysisPt(sta)->pathMinMax();
}
const EarlyLate *
PathEnd::pathEarlyLate(const StaState *sta) const
{
return path_->pathAnalysisPt(sta)->pathMinMax();
}
const EarlyLate *
PathEnd::clkEarlyLate(const StaState *sta) const
{
return checkRole(sta)->tgtClkEarlyLate();
}
const RiseFall *
PathEnd::transition(const StaState *sta) const
{
return path_->transition(sta);
}
PathAPIndex
PathEnd::pathIndex(const StaState *sta) const
{
return path_->pathAnalysisPtIndex(sta);
}
PathAnalysisPt *
PathEnd::pathAnalysisPt(const StaState *sta) const
{
return path_->pathAnalysisPt(sta);
}
const ClockEdge *
PathEnd::sourceClkEdge(const StaState *sta) const
{
return path_->clkEdge(sta);
}
Arrival
PathEnd::dataArrivalTime(const StaState *) const
{
return path_->arrival();
}
Arrival
PathEnd::dataArrivalTimeOffset(const StaState *sta) const
{
return dataArrivalTime(sta) + sourceClkOffset(sta);
}
Required
PathEnd::requiredTimeOffset(const StaState *sta) const
{
return requiredTime(sta) + sourceClkOffset(sta);
}
const RiseFall *
PathEnd::targetClkEndTrans(const StaState *sta) const
{
const Path *clk_path = targetClkPath();
if (clk_path)
return clk_path->transition(sta);
else {
const ClockEdge *clk_edge = targetClkEdge(sta);
if (clk_edge)
return clk_edge->transition();
else
return nullptr;
}
}
const TimingRole *
PathEnd::checkGenericRole(const StaState *sta) const
{
return checkRole(sta)->genericRole();
}
Delay
PathEnd::sourceClkLatency(const StaState *) const
{
return delay_zero;
}
Delay
PathEnd::sourceClkInsertionDelay(const StaState *) const
{
return delay_zero;
}
const Clock *
PathEnd::targetClk(const StaState *) const
{
return nullptr;
}
const ClockEdge *
PathEnd::targetClkEdge(const StaState *) const
{
return nullptr;
}
float
PathEnd::targetClkTime(const StaState *) const
{
return 0.0;
}
float
PathEnd::targetClkOffset(const StaState *) const
{
return 0.0;
}
Arrival
PathEnd::targetClkArrival(const StaState *) const
{
return delay_zero;
}
Delay
PathEnd::targetClkDelay(const StaState *) const
{
return delay_zero;
}
Delay
PathEnd::targetClkInsertionDelay(const StaState *) const
{
return delay_zero;
}
float
PathEnd::targetNonInterClkUncertainty(const StaState *) const
{
return 0.0;
}
float
PathEnd::interClkUncertainty(const StaState *) const
{
return 0.0;
}
float
PathEnd::targetClkUncertainty(const StaState *) const
{
return 0.0;
}
float
PathEnd::targetClkMcpAdjustment(const StaState *) const
{
return 0.0;
}
const TimingRole *
PathEnd::checkRole(const StaState *) const
{
return nullptr;
}
Path *
PathEnd::targetClkPath()
{
return nullptr;
}
const Path *
PathEnd::targetClkPath() const
{
return nullptr;
}
bool
PathEnd::pathDelayMarginIsExternal() const
{
return false;
}
PathDelay *
PathEnd::pathDelay() const
{
return nullptr;
}
Arrival
PathEnd::borrow(const StaState *) const
{
return 0.0;
}
Crpr
PathEnd::checkCrpr(const StaState *sta) const
{
if (checkRole(sta)->genericRole() == TimingRole::hold())
return -crpr(sta);
else
return crpr(sta);;
}
Crpr
PathEnd::crpr(const StaState *) const
{
return 0.0;
}
MultiCyclePath *
PathEnd::multiCyclePath() const
{
return nullptr;
}
int
PathEnd::exceptPathCmp(const PathEnd *path_end,
const StaState *) const
{
Type type1 = type();
Type type2 = path_end->type();
if (type1 == type2)
return 0;
else if (type1 < type2)
return -1;
else
return 1;
}
////////////////////////////////////////////////////////////////
Delay
PathEnd::checkTgtClkDelay(const Path *tgt_clk_path,
const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
const StaState *sta)
{
Delay insertion, latency;
checkTgtClkDelay(tgt_clk_path, tgt_clk_edge, check_role, sta,
insertion, latency);
return Delay(insertion + latency);
}
void
PathEnd::checkTgtClkDelay(const Path *tgt_clk_path,
const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
const StaState *sta,
// Return values.
Delay &insertion,
Delay &latency)
{
if (tgt_clk_path) {
Search *search = sta->search();
// If propagated clk, adjust required time for target clk network delay.
const MinMax *min_max = tgt_clk_path->minMax(sta);
const EarlyLate *early_late = check_role->tgtClkEarlyLate();
const PathAnalysisPt *tgt_path_ap = tgt_clk_path->pathAnalysisPt(sta);
const ClkInfo *clk_info = tgt_clk_path->clkInfo(sta);
const Pin *tgt_src_pin = clk_info->clkSrc();
const Clock *tgt_clk = tgt_clk_edge->clock();
const RiseFall *tgt_clk_rf = tgt_clk_edge->transition();
insertion = search->clockInsertion(tgt_clk, tgt_src_pin, tgt_clk_rf,
min_max, early_late, tgt_path_ap);
if (clk_info->isPropagated()
// Data check target clock is always propagated.
|| check_role->isDataCheck()) {
// Propagated clock. Propagated arrival is seeded with
// early_late==path_min_max insertion delay.
Arrival clk_arrival = tgt_clk_path->arrival();
Delay path_insertion = search->clockInsertion(tgt_clk, tgt_src_pin,
tgt_clk_rf, min_max,
min_max, tgt_path_ap);
latency = delayRemove(clk_arrival - tgt_clk_edge->time(), path_insertion);
}
else
// Ideal clock.
latency = clk_info->latency();
}
else {
insertion = 0.0F;
latency = 0.0F;
}
}
float
PathEnd::checkClkUncertainty(const ClockEdge *src_clk_edge,
const ClockEdge *tgt_clk_edge,
const Path *tgt_clk_path,
const TimingRole *check_role,
const StaState *sta)
{
float inter_clk;
bool inter_exists;
checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role, sta,
inter_clk, inter_exists);
if (inter_exists)
return inter_clk;
else
return checkTgtClkUncertainty(tgt_clk_path, tgt_clk_edge, check_role, sta);
}
float
PathEnd::checkTgtClkUncertainty(const Path *tgt_clk_path,
const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
const StaState *sta)
{
const MinMax *min_max = check_role->pathMinMax();
ClockUncertainties *uncertainties = nullptr;
if (tgt_clk_path && tgt_clk_path->isClock(sta))
uncertainties = tgt_clk_path->clkInfo(sta)->uncertainties();
else if (tgt_clk_edge)
uncertainties = tgt_clk_edge->clock()->uncertainties();
float uncertainty = 0.0;
if (uncertainties) {
bool exists;
float uncertainty1;
uncertainties->value(min_max, uncertainty1, exists);
if (exists)
uncertainty = uncertainty1;
}
if (check_role->genericRole() == TimingRole::setup())
uncertainty = -uncertainty;
return uncertainty;
}
void
PathEnd::checkInterClkUncertainty(const ClockEdge *src_clk_edge,
const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
const StaState *sta,
float &uncertainty,
bool &exists)
{
Sdc *sdc = sta->sdc();
if (src_clk_edge
&& src_clk_edge != sdc->defaultArrivalClockEdge()
&& tgt_clk_edge) {
sdc->clockUncertainty(src_clk_edge->clock(),
src_clk_edge->transition(),
tgt_clk_edge->clock(),
tgt_clk_edge->transition(),
check_role->pathMinMax(),
uncertainty, exists);
if (exists
&& check_role->genericRole() == TimingRole::setup())
uncertainty = -uncertainty;
}
else
exists = false;
}
////////////////////////////////////////////////////////////////
void
PathEndUnconstrained::reportFull(const ReportPath *report) const
{
report->reportFull(this);
}
Slack
PathEndUnconstrained::slackNoCrpr(const StaState *) const
{
return INF;
}
void
PathEndUnconstrained::reportShort(const ReportPath *report) const
{
report->reportShort(this);
}
////////////////////////////////////////////////////////////////
PathEndUnconstrained::PathEndUnconstrained(Path *path) :
PathEnd(path)
{
}
PathEnd *
PathEndUnconstrained::copy() const
{
return new PathEndUnconstrained(path_);
}
bool
PathEndUnconstrained::isUnconstrained() const
{
return true;
}
Required
PathEndUnconstrained::requiredTime(const StaState *sta) const
{
return delayInitValue(minMax(sta)->opposite());
}
Required
PathEndUnconstrained::requiredTimeOffset(const StaState *sta) const
{
return delayInitValue(minMax(sta)->opposite());
}
ArcDelay
PathEndUnconstrained::margin(const StaState *) const
{
return delay_zero;
}
Slack
PathEndUnconstrained::slack(const StaState *) const
{
return INF;
}
float
PathEndUnconstrained::sourceClkOffset(const StaState *) const
{
return 0.0;
}
PathEnd::Type
PathEndUnconstrained::type() const
{
return Type::unconstrained;
}
const char *
PathEndUnconstrained::typeName() const
{
return "unconstrained";
}
////////////////////////////////////////////////////////////////
PathEndClkConstrained::PathEndClkConstrained(Path *path,
Path *clk_path) :
PathEnd(path),
clk_path_(clk_path),
crpr_(0.0),
crpr_valid_(false)
{
}
PathEndClkConstrained::PathEndClkConstrained(Path *path,
Path *clk_path,
Crpr crpr,
bool crpr_valid) :
PathEnd(path),
clk_path_(clk_path),
crpr_(crpr),
crpr_valid_(crpr_valid)
{
}
void
PathEndClkConstrained::setPath(Path *path)
{
path_ = path;
crpr_valid_ = false;
}
float
PathEndClkConstrained::sourceClkOffset(const StaState *sta) const
{
return sourceClkOffset(sourceClkEdge(sta),
targetClkEdge(sta),
checkRole(sta),
sta);
}
float
PathEndClkConstrained::sourceClkOffset(const ClockEdge *src_clk_edge,
const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
const StaState *sta) const
{
Sdc *sdc = sta->sdc();
CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge);
return acct->sourceTimeOffset(check_role);
}
Arrival
PathEndClkConstrained::sourceClkLatency(const StaState *sta) const
{
const ClkInfo *clk_info = path_->clkInfo(sta);
return clk_info->latency();
}
Arrival
PathEndClkConstrained::sourceClkInsertionDelay(const StaState *sta) const
{
const ClkInfo *clk_info = path_->clkInfo(sta);
return clk_info->insertion();
}
Path *
PathEndClkConstrained::targetClkPath()
{
return clk_path_;
}
const Path *
PathEndClkConstrained::targetClkPath() const
{
return clk_path_;
}
float
PathEndClkConstrained::targetClkOffset(const StaState *sta) const
{
const ClockEdge *src_clk_edge = sourceClkEdge(sta);
const ClockEdge *tgt_clk_edge = targetClkEdge(sta);
const TimingRole *check_role = checkRole(sta);
Sdc *sdc = sta->sdc();
CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge);
return acct->targetTimeOffset(check_role);
}
const ClockEdge *
PathEndClkConstrained::targetClkEdge(const StaState *sta) const
{
if (clk_path_)
return clk_path_->clkEdge(sta);
else
return nullptr;
}
const Clock *
PathEndClkConstrained::targetClk(const StaState *sta) const
{
const ClockEdge *clk_edge = targetClkEdge(sta);
if (clk_edge)
return clk_edge->clock();
else
return nullptr;
}
float
PathEndClkConstrained::targetClkTime(const StaState *sta) const
{
const ClockEdge *src_clk_edge = sourceClkEdge(sta);
const ClockEdge *tgt_clk_edge = targetClkEdge(sta);
const TimingRole *check_role = checkRole(sta);
Sdc *sdc = sta->sdc();
CycleAccting *acct = sdc->cycleAccting(src_clk_edge, tgt_clk_edge);
return acct->requiredTime(check_role);
}
Arrival
PathEndClkConstrained::targetClkArrival(const StaState *sta) const
{
return targetClkArrivalNoCrpr(sta)
+ checkCrpr(sta);
}
Arrival
PathEndClkConstrained::targetClkArrivalNoCrpr(const StaState *sta) const
{
return targetClkTime(sta)
+ targetClkDelay(sta)
+ checkClkUncertainty(sourceClkEdge(sta),
targetClkEdge(sta),
targetClkPath(),
checkRole(sta), sta)
+ targetClkMcpAdjustment(sta);
}
Delay
PathEndClkConstrained::targetClkDelay(const StaState *sta) const
{
return checkTgtClkDelay(targetClkPath(), targetClkEdge(sta), checkRole(sta), sta);
}
Delay
PathEndClkConstrained::targetClkInsertionDelay(const StaState *sta) const
{
Arrival insertion, latency;
checkTgtClkDelay(targetClkPath(), targetClkEdge(sta),
checkRole(sta), sta,
insertion, latency);
return insertion;
}
float
PathEndClkConstrained::targetNonInterClkUncertainty(const StaState *sta) const
{
const ClockEdge *src_clk_edge = sourceClkEdge(sta);
const ClockEdge *tgt_clk_edge = targetClkEdge(sta);
const TimingRole *check_role = checkRole(sta);
float inter_clk;
bool inter_exists;
checkInterClkUncertainty(src_clk_edge, tgt_clk_edge, check_role,
sta, inter_clk, inter_exists);
if (inter_exists)
// This returns non inter-clock uncertainty.
return 0.0;
else
return checkTgtClkUncertainty(targetClkPath(), tgt_clk_edge, check_role, sta);
}
float
PathEndClkConstrained::interClkUncertainty(const StaState *sta) const
{
float uncertainty;
bool exists;
checkInterClkUncertainty(sourceClkEdge(sta), targetClkEdge(sta),
checkRole(sta), sta,
uncertainty, exists);
if (exists)
return uncertainty;
else
return 0.0;
}
float
PathEndClkConstrained::targetClkUncertainty(const StaState *sta) const
{
return checkClkUncertainty(sourceClkEdge(sta), targetClkEdge(sta),
targetClkPath(), checkRole(sta), sta);
}
Crpr
PathEndClkConstrained::crpr(const StaState *sta) const
{
if (!crpr_valid_) {
CheckCrpr *check_crpr = sta->search()->checkCrpr();
crpr_ = check_crpr->checkCrpr(path_, targetClkPath());
crpr_valid_ = true;
}
return crpr_;
}
Required
PathEndClkConstrained::requiredTime(const StaState *sta) const
{
return requiredTimeNoCrpr(sta)
+ checkCrpr(sta);
}
Required
PathEndClkConstrained::requiredTimeNoCrpr(const StaState *sta) const
{
Arrival tgt_clk_arrival = targetClkArrivalNoCrpr(sta);
ArcDelay check_margin = margin(sta);
if (checkGenericRole(sta) == TimingRole::setup())
return tgt_clk_arrival - check_margin;
else
return tgt_clk_arrival + check_margin;
}
Slack
PathEndClkConstrained::slack(const StaState *sta) const
{
Arrival arrival = dataArrivalTime(sta);
Required required = requiredTime(sta);
if (checkGenericRole(sta) == TimingRole::setup())
return required - arrival;
else
return arrival - required;
}
int
PathEndClkConstrained::exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const
{
int cmp = PathEnd::exceptPathCmp(path_end, sta);
if (cmp == 0) {
const PathEndClkConstrained *path_end2 =
dynamic_cast(path_end);
const Path *clk_path2 = path_end2->targetClkPath();
return Path::cmp(targetClkPath(), clk_path2, sta);
}
else
return cmp;
}
////////////////////////////////////////////////////////////////
PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path,
Path *clk_path,
MultiCyclePath *mcp) :
PathEndClkConstrained(path, clk_path),
mcp_(mcp)
{
}
PathEndClkConstrainedMcp::PathEndClkConstrainedMcp(Path *path,
Path *clk_path,
MultiCyclePath *mcp,
Crpr crpr,
bool crpr_valid) :
PathEndClkConstrained(path, clk_path, crpr, crpr_valid),
mcp_(mcp)
{
}
float
PathEndClkConstrainedMcp::targetClkMcpAdjustment(const StaState *sta) const
{
return checkMcpAdjustment(path_, targetClkEdge(sta), sta);
}
float
PathEndClkConstrainedMcp::checkMcpAdjustment(const Path *path,
const ClockEdge *tgt_clk_edge,
const StaState *sta) const
{
if (mcp_) {
const TimingRole *check_role = checkRole(sta);
const MinMax *min_max = check_role->pathMinMax();
const ClockEdge *src_clk_edge = path->clkEdge(sta);
Sdc *sdc = sta->sdc();
if (min_max == MinMax::max())
return PathEnd::checkSetupMcpAdjustment(src_clk_edge, tgt_clk_edge,
mcp_, setupDefaultCycles(), sdc);
else {
// Hold check.
// Default arrival clock is a proxy for the target clock.
if (src_clk_edge == nullptr)
src_clk_edge = tgt_clk_edge;
else if (src_clk_edge->clock() == sdc->defaultArrivalClock())
src_clk_edge = tgt_clk_edge->clock()->edge(src_clk_edge->transition());
const MultiCyclePath *setup_mcp;
const MultiCyclePath *hold_mcp;
// Hold checks also need the setup mcp for cycle accounting.
findHoldMcps(tgt_clk_edge, setup_mcp, hold_mcp, sta);
if (setup_mcp && hold_mcp) {
int setup_mult = setup_mcp->pathMultiplier(MinMax::max());
int hold_mult = hold_mcp->pathMultiplier(MinMax::min());
const ClockEdge *setup_clk_edge =
setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge;
float setup_period = setup_clk_edge->clock()->period();
const ClockEdge *hold_clk_edge =
hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge;
float hold_period = hold_clk_edge->clock()->period();
return (setup_mult - 1) * setup_period - hold_mult * hold_period;
}
else if (hold_mcp) {
int mult = hold_mcp->pathMultiplier(min_max);
const ClockEdge *clk_edge =
hold_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge;
float period = clk_edge->clock()->period();
return -mult * period;
}
else if (setup_mcp) {
int mult = setup_mcp->pathMultiplier(min_max);
const ClockEdge *clk_edge =
setup_mcp->useEndClk() ? tgt_clk_edge : src_clk_edge;
float period = clk_edge->clock()->period();
return (mult - 1) * period;
}
else
return 0.0;
}
}
else
return 0.0;
}
float
PathEnd::checkSetupMcpAdjustment(const ClockEdge *src_clk_edge,
const ClockEdge *tgt_clk_edge,
const MultiCyclePath *mcp,
int default_cycles,
Sdc *sdc)
{
if (mcp) {
// Default arrival clock is a proxy for the target clock.
if (src_clk_edge == nullptr)
src_clk_edge = tgt_clk_edge;
else if (src_clk_edge->clock() == sdc->defaultArrivalClock())
src_clk_edge = tgt_clk_edge->clock()->edge(src_clk_edge->transition());
if (mcp->minMax()->matches(MinMax::max())) {
int mult = mcp->pathMultiplier(MinMax::max());
const ClockEdge *clk_edge =
mcp->useEndClk() ? tgt_clk_edge : src_clk_edge;
float period = clk_edge->clock()->period();
return (mult - default_cycles) * period;
}
else
return 0.0;
}
else
return 0.0;
}
Slack
PathEndClkConstrained::slackNoCrpr(const StaState *sta) const
{
Arrival arrival = dataArrivalTime(sta);
Required required = requiredTimeNoCrpr(sta);
if (checkGenericRole(sta) == TimingRole::setup())
return required - arrival;
else
return arrival - required;
}
void
PathEndClkConstrainedMcp::findHoldMcps(const ClockEdge *tgt_clk_edge,
const MultiCyclePath *&setup_mcp,
const MultiCyclePath *&hold_mcp,
const StaState *sta) const
{
Pin *pin = path_->pin(sta);
const RiseFall *rf = path_->transition(sta);
// Mcp may be setup, hold or setup_hold, since all match min paths.
const MinMaxAll *mcp_min_max = mcp_->minMax();
Search *search = sta->search();
if (mcp_min_max->matches(MinMax::min())) {
hold_mcp = mcp_;
setup_mcp =
dynamic_cast(search->exceptionTo(ExceptionPathType::multi_cycle,
path_, pin, rf,
tgt_clk_edge,
MinMax::max(), true,
false));
}
else {
setup_mcp = mcp_;
hold_mcp =
dynamic_cast(search->exceptionTo(ExceptionPathType::multi_cycle,
path_, pin, rf,
tgt_clk_edge,
MinMax::min(), true,
false));
}
}
int
PathEndClkConstrainedMcp::exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const
{
int cmp = PathEndClkConstrained::exceptPathCmp(path_end, sta);
if (cmp == 0) {
const PathEndClkConstrainedMcp *path_end2 =
dynamic_cast(path_end);
const MultiCyclePath *mcp2 = path_end2->mcp_;
if (mcp_ == mcp2)
return 0;
else if (mcp_ < mcp2)
return -1;
else
return 1;
}
else
return cmp;
}
////////////////////////////////////////////////////////////////
PathEndCheck::PathEndCheck(Path *path,
TimingArc *check_arc,
Edge *check_edge,
Path *clk_path,
MultiCyclePath *mcp,
const StaState *) :
PathEndClkConstrainedMcp(path, clk_path, mcp),
check_arc_(check_arc),
check_edge_(check_edge)
{
}
PathEndCheck::PathEndCheck(Path *path,
TimingArc *check_arc,
Edge *check_edge,
Path *clk_path,
MultiCyclePath *mcp,
Crpr crpr,
bool crpr_valid) :
PathEndClkConstrainedMcp(path, clk_path, mcp, crpr, crpr_valid),
check_arc_(check_arc),
check_edge_(check_edge)
{
}
PathEnd *
PathEndCheck::copy() const
{
return new PathEndCheck(path_, check_arc_, check_edge_,
clk_path_, mcp_, crpr_, crpr_valid_);
}
PathEnd::Type
PathEndCheck::type() const
{
return Type::check;
}
const char *
PathEndCheck::typeName() const
{
return "check";
}
void
PathEndCheck::reportFull(const ReportPath *report) const
{
report->reportFull(this);
}
void
PathEndCheck::reportShort(const ReportPath *report) const
{
report->reportShort(this);
}
const TimingRole *
PathEndCheck::checkRole(const StaState *) const
{
return check_edge_->role();
}
ArcDelay
PathEndCheck::margin(const StaState *sta) const
{
return sta->search()->deratedDelay(clk_path_->vertex(sta),
check_arc_, check_edge_, false,
pathAnalysisPt(sta));
}
int
PathEndCheck::exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const
{
int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta);
if (cmp == 0) {
const PathEndCheck *path_end2=dynamic_cast(path_end);
const TimingArc *check_arc2 = path_end2->check_arc_;
if (check_arc_ == check_arc2)
return 0;
else if (check_arc_ < check_arc2)
return -1;
else
return 1;
}
else
return cmp;
}
Delay
PathEndCheck::clkSkew(const StaState *sta)
{
return sourceClkDelay(sta) - targetClkDelay(sta) - crpr(sta)
// Uncertainty decreases slack, but increases skew.
- checkTgtClkUncertainty(clk_path_, clk_path_->clkEdge(sta),
checkRole(sta), sta);
}
Delay
PathEndCheck::sourceClkDelay(const StaState *sta) const
{
PathExpanded expanded(path_, sta);
const Path *src_clk_path = expanded.clkPath();
if (src_clk_path) {
const ClkInfo *src_clk_info = path_->tag(sta)->clkInfo();
if (src_clk_info->isPropagated()) {
// Propagated clock. Propagated arrival is seeded with insertion delay.
Arrival clk_arrival = src_clk_path->arrival();
const ClockEdge *src_clk_edge = src_clk_info->clkEdge();
Delay insertion = sourceClkInsertionDelay(sta);
return delayRemove(clk_arrival - src_clk_edge->time(), insertion);
}
else
// Ideal clock.
return sourceClkLatency(sta);
}
else
return 0.0;
}
Required
PathEndCheck::requiredTimeNoCrpr(const StaState *sta) const
{
Arrival tgt_clk_arrival = targetClkArrivalNoCrpr(sta);
ArcDelay check_margin = margin(sta);
float macro_clk_tree_delay = macroClkTreeDelay(sta);
if (checkGenericRole(sta) == TimingRole::setup())
return tgt_clk_arrival - (check_margin + macro_clk_tree_delay);
else
return tgt_clk_arrival + (check_margin - macro_clk_tree_delay);
}
float
PathEndCheck::macroClkTreeDelay(const StaState *sta) const
{
const ClockEdge *tgt_clk_edge = targetClkEdge(sta);
const Clock *tgt_clk = tgt_clk_edge->clock();
const Network *network = sta->network();
const Pin *clk_pin = clk_path_->pin(sta);
const Instance *inst = network->instance(clk_pin);
const LibertyCell *inst_cell = network->libertyCell(inst);
if (tgt_clk->isIdeal()
&& inst_cell && inst_cell->isMacro()) {
LibertyPort *clk_port = network->libertyPort(clk_pin);
if (clk_port) {
const MinMax *min_max = clk_path_->minMax(sta);
const RiseFall *rf = clk_path_->transition(sta);
float slew = delayAsFloat(clk_path_->slew(sta));
return clk_port->clkTreeDelay(slew, rf, min_max);
}
}
return 0.0;
}
////////////////////////////////////////////////////////////////
PathEndLatchCheck::PathEndLatchCheck(Path *path,
TimingArc *check_arc,
Edge *check_edge,
Path *disable_path,
MultiCyclePath *mcp,
PathDelay *path_delay,
const StaState *sta) :
PathEndCheck(path, check_arc, check_edge, nullptr, mcp, sta),
disable_path_(disable_path),
path_delay_(path_delay),
src_clk_arrival_(0.0)
{
Latches *latches = sta->latches();
Path *enable_path =
latches->latchEnableOtherPath(disable_path,
disable_path->pathAnalysisPt(sta));
clk_path_ = enable_path;
Search *search = sta->search();
// Same as PathEndPathDelay::findRequired.
if (path_delay_ && ignoreClkLatency(sta))
src_clk_arrival_ = search->pathClkPathArrival(path_);
}
PathEndLatchCheck::PathEndLatchCheck(Path *path,
TimingArc *check_arc,
Edge *check_edge,
Path *clk_path,
Path *disable_path,
MultiCyclePath *mcp,
PathDelay *path_delay,
Delay src_clk_arrival,
Crpr crpr,
bool crpr_valid) :
PathEndCheck(path, check_arc, check_edge, clk_path, mcp, crpr, crpr_valid),
disable_path_(disable_path),
path_delay_(path_delay),
src_clk_arrival_(src_clk_arrival)
{
}
PathEnd *
PathEndLatchCheck::copy() const
{
return new PathEndLatchCheck(path_, check_arc_, check_edge_,
clk_path_, disable_path_, mcp_, path_delay_,
src_clk_arrival_, crpr_, crpr_valid_);
}
PathEnd::Type
PathEndLatchCheck::type() const
{
return Type::latch_check;
}
const char *
PathEndLatchCheck::typeName() const
{
return "latch_check";
}
Path *
PathEndLatchCheck::latchDisable()
{
return disable_path_;
}
const Path *
PathEndLatchCheck::latchDisable() const
{
return disable_path_;
}
void
PathEndLatchCheck::reportFull(const ReportPath *report) const
{
report->reportFull(this);
}
void
PathEndLatchCheck::reportShort(const ReportPath *report) const
{
report->reportShort(this);
}
float
PathEndLatchCheck::sourceClkOffset(const StaState *sta) const
{
if (path_delay_)
return pathDelaySrcClkOffset(path_, path_delay_, src_clk_arrival_, sta);
else
return PathEndClkConstrained::sourceClkOffset(sourceClkEdge(sta),
disable_path_->clkEdge(sta),
TimingRole::setup(),
sta);
}
const TimingRole *
PathEndLatchCheck::checkRole(const StaState *sta) const
{
if (clk_path_->clkInfo(sta)->isPulseClk())
// Pulse latches use register cycle accounting.
return TimingRole::setup();
else
// Setup cycle accting is slightly different because it is wrt
// the enable opening edge, not the disable (setup check) edge.
return TimingRole::latchSetup();
}
float
PathEndLatchCheck::targetClkTime(const StaState *sta) const
{
if (path_delay_)
return 0.0;
else
return PathEndClkConstrained::targetClkTime(sta);
}
float
PathEndLatchCheck::targetClkOffset(const StaState *sta) const
{
if (path_delay_)
return -targetClkEdge(sta)->time();
else
return PathEndClkConstrained::targetClkOffset(sta);
}
Required
PathEndLatchCheck::requiredTime(const StaState *sta) const
{
Required required;
Arrival borrow, adjusted_data_arrival, time_given_to_startpoint;
Latches *latches = sta->latches();
latches->latchRequired(path_, targetClkPath(), latchDisable(),
mcp_, path_delay_, src_clk_arrival_, margin(sta),
required, borrow, adjusted_data_arrival,
time_given_to_startpoint);
return required;
}
Arrival
PathEndLatchCheck::borrow(const StaState *sta) const
{
Latches *latches = sta->latches();
Required required;
Arrival borrow, adjusted_data_arrival, time_given_to_startpoint;
latches->latchRequired(path_, targetClkPath(), latchDisable(),
mcp_, path_delay_, src_clk_arrival_, margin(sta),
required, borrow, adjusted_data_arrival,
time_given_to_startpoint);
return borrow;
}
void
PathEndLatchCheck::latchRequired(const StaState *sta,
// Return values.
Required &required,
Delay &borrow,
Arrival &adjusted_data_arrival,
Delay &time_given_to_startpoint) const
{
Latches *latches = sta->latches();
latches->latchRequired(path_, targetClkPath(), latchDisable(),
mcp_, path_delay_, src_clk_arrival_, margin(sta),
required, borrow, adjusted_data_arrival,
time_given_to_startpoint);
}
void
PathEndLatchCheck::latchBorrowInfo(const StaState *sta,
// Return values.
float &nom_pulse_width,
Delay &open_latency,
Delay &latency_diff,
float &open_uncertainty,
Crpr &open_crpr,
Crpr &crpr_diff,
Delay &max_borrow,
bool &borrow_limit_exists) const
{
Latches *latches = sta->latches();
latches->latchBorrowInfo(path_, targetClkPath(), latchDisable(),
margin(sta),
path_delay_ && ignoreClkLatency(sta),
nom_pulse_width, open_latency,
latency_diff, open_uncertainty,
open_crpr, crpr_diff, max_borrow,
borrow_limit_exists);
}
Arrival
PathEndLatchCheck::targetClkWidth(const StaState *sta) const
{
const Search *search = sta->search();
Arrival disable_arrival = search->clkPathArrival(disable_path_);
Arrival enable_arrival = search->clkPathArrival(clk_path_);
const ClkInfo *enable_clk_info = clk_path_->clkInfo(sta);
if (enable_clk_info->isPulseClk())
return disable_arrival - enable_arrival;
else {
if (delayGreater(enable_arrival, disable_arrival, sta)) {
const Clock *disable_clk = enable_clk_info->clock();
if (disable_clk)
disable_arrival += disable_clk->period();
}
return disable_arrival - enable_arrival;
}
}
int
PathEndLatchCheck::exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const
{
int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta);
if (cmp == 0) {
const PathEndLatchCheck *path_end2 =
dynamic_cast(path_end);
const TimingArc *check_arc2 = path_end2->check_arc_;
if (check_arc_ == check_arc2) {
const Path *disable_path2 = path_end2->disable_path_;
return Path::cmp(disable_path_, disable_path2, sta);
}
else if (check_arc_ < check_arc2)
return -1;
else
return 1;
}
else
return cmp;
}
bool
PathEndLatchCheck::ignoreClkLatency(const StaState *sta) const
{
return PathEnd::ignoreClkLatency(path_, path_delay_, sta);
}
///////////////////////////////////////////////////////////////
PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay,
Path *path,
Path *clk_path,
MultiCyclePath *mcp,
const StaState *) :
// No target clk_path_ for output delays.
PathEndClkConstrainedMcp(path, clk_path, mcp),
output_delay_(output_delay)
{
}
PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay,
Path *path,
Path *clk_path,
MultiCyclePath *mcp,
Crpr crpr,
bool crpr_valid) :
PathEndClkConstrainedMcp(path, clk_path, mcp, crpr, crpr_valid),
output_delay_(output_delay)
{
}
PathEnd *
PathEndOutputDelay::copy() const
{
return new PathEndOutputDelay(output_delay_, path_, clk_path_,
mcp_, crpr_, crpr_valid_);
}
PathEnd::Type
PathEndOutputDelay::type() const
{
return Type::output_delay;
}
const char *
PathEndOutputDelay::typeName() const
{
return "output_delay";
}
void
PathEndOutputDelay::reportFull(const ReportPath *report) const
{
report->reportFull(this);
}
void
PathEndOutputDelay::reportShort(const ReportPath *report) const
{
report->reportShort(this);
}
ArcDelay
PathEndOutputDelay::margin(const StaState *sta) const
{
return outputDelayMargin(output_delay_, path_, sta);
}
float
PathEnd::outputDelayMargin(OutputDelay *output_delay,
const Path *path,
const StaState *sta)
{
const RiseFall *rf = path->transition(sta);
const MinMax *min_max = path->minMax(sta);
float margin = output_delay->delays()->value(rf, min_max);
if (min_max == MinMax::max())
return margin;
else
return -margin;
}
const TimingRole *
PathEndOutputDelay::checkRole(const StaState *sta) const
{
if (path_->minMax(sta) == MinMax::max())
return TimingRole::outputSetup();
else
return TimingRole::outputHold();
}
const ClockEdge *
PathEndOutputDelay::targetClkEdge(const StaState *sta) const
{
if (clk_path_)
return clk_path_->clkEdge(sta);
else
return output_delay_->clkEdge();
}
Arrival
PathEndOutputDelay::targetClkArrivalNoCrpr(const StaState *sta) const
{
if (clk_path_)
return PathEndClkConstrained::targetClkArrivalNoCrpr(sta);
else {
const ClockEdge *tgt_clk_edge = targetClkEdge(sta);
const TimingRole *check_role = checkRole(sta);
return targetClkTime(sta)
+ tgtClkDelay(tgt_clk_edge, check_role, sta)
+ targetClkUncertainty(sta)
+ checkMcpAdjustment(path_, tgt_clk_edge, sta);
}
}
Crpr
PathEndOutputDelay::crpr(const StaState *sta) const
{
if (!crpr_valid_) {
CheckCrpr *check_crpr = sta->search()->checkCrpr();
crpr_ = check_crpr->outputDelayCrpr(path_, targetClkEdge(sta));
crpr_valid_ = true;
}
return crpr_;
}
Delay
PathEndOutputDelay::targetClkDelay(const StaState *sta) const
{
if (clk_path_)
return PathEndClkConstrained::targetClkDelay(sta);
else
return tgtClkDelay(targetClkEdge(sta), checkRole(sta), sta);
}
Arrival
PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
const StaState *sta) const
{
Arrival insertion, latency;
tgtClkDelay(tgt_clk_edge, check_role, sta,
insertion, latency);
return insertion + latency;
}
void
PathEndOutputDelay::tgtClkDelay(const ClockEdge *tgt_clk_edge,
const TimingRole *check_role,
const StaState *sta,
// Return values.
Arrival &insertion,
Arrival &latency) const
{
// Early late: setup early, hold late.
const EarlyLate *early_late = check_role->tgtClkEarlyLate();
// Latency min_max depends on bc_wc or ocv.
const PathAnalysisPt *path_ap = path_->pathAnalysisPt(sta);
const MinMax *latency_min_max = path_ap->tgtClkAnalysisPt()->pathMinMax();
Clock *tgt_clk = tgt_clk_edge->clock();
const RiseFall *tgt_clk_rf = tgt_clk_edge->transition();
if (!output_delay_->sourceLatencyIncluded())
insertion = sta->search()->clockInsertion(tgt_clk,
tgt_clk->defaultPin(),
tgt_clk_rf,
latency_min_max,
early_late, path_ap);
else
insertion = 0.0;
const Sdc *sdc = sta->sdc();
if (!tgt_clk->isPropagated()
&& !output_delay_->networkLatencyIncluded())
latency = sdc->clockLatency(tgt_clk, tgt_clk_rf, latency_min_max);
else
latency = 0.0;
}
Delay
PathEndOutputDelay::targetClkInsertionDelay(const StaState *sta) const
{
if (clk_path_)
return PathEndClkConstrained::targetClkInsertionDelay(sta);
else {
Arrival insertion, latency;
tgtClkDelay(targetClkEdge(sta), checkRole(sta), sta,
insertion, latency);
return insertion;
}
}
int
PathEndOutputDelay::exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const
{
int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta);
if (cmp == 0) {
const PathEndOutputDelay *path_end2 =
dynamic_cast(path_end);
OutputDelay *output_delay2 = path_end2->output_delay_;
if (output_delay_ == output_delay2)
return 0;
else if (output_delay_ < output_delay2)
return -1;
else
return 1;
}
else
return cmp;
}
////////////////////////////////////////////////////////////////
PathEndGatedClock::PathEndGatedClock(Path *gating_ref,
Path *clk_path,
const TimingRole *check_role,
MultiCyclePath *mcp,
ArcDelay margin,
const StaState *) :
PathEndClkConstrainedMcp(gating_ref, clk_path, mcp),
check_role_(check_role),
margin_(margin)
{
}
PathEndGatedClock::PathEndGatedClock(Path *gating_ref,
Path *clk_path,
const TimingRole *check_role,
MultiCyclePath *mcp,
ArcDelay margin,
Crpr crpr,
bool crpr_valid) :
PathEndClkConstrainedMcp(gating_ref, clk_path, mcp, crpr, crpr_valid),
check_role_(check_role),
margin_(margin)
{
}
PathEnd *
PathEndGatedClock::copy() const
{
return new PathEndGatedClock(path_, clk_path_, check_role_,
mcp_, margin_, crpr_, crpr_valid_);
}
PathEnd::Type
PathEndGatedClock::type() const
{
return Type::gated_clk;
}
const char *
PathEndGatedClock::typeName() const
{
return "gated_clk";
}
const TimingRole *
PathEndGatedClock::checkRole(const StaState *) const
{
return check_role_;
}
void
PathEndGatedClock::reportFull(const ReportPath *report) const
{
report->reportFull(this);
}
void
PathEndGatedClock::reportShort(const ReportPath *report) const
{
report->reportShort(this);
}
int
PathEndGatedClock::exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const
{
int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta);
if (cmp == 0) {
const PathEndGatedClock *path_end2 =
dynamic_cast(path_end);
const TimingRole *check_role2 = path_end2->check_role_;
if (check_role_ == check_role2)
return 0;
else if (check_role_ < check_role2)
return -1;
else
return 1;
}
else
return cmp;
}
////////////////////////////////////////////////////////////////
PathEndDataCheck::PathEndDataCheck(DataCheck *check,
Path *data_path,
Path *data_clk_path,
MultiCyclePath *mcp,
const StaState *sta) :
PathEndClkConstrainedMcp(data_path, nullptr, mcp),
data_clk_path_(data_clk_path),
check_(check)
{
clk_path_ = clkPath(data_clk_path, sta);
}
// PathExpanded::expand() and PathExpanded::clkPath().
Path *
PathEndDataCheck::clkPath(Path *path,
const StaState *sta)
{
Path *p = path;
while (p) {
Path *prev_path = p->prevPath();
TimingArc *prev_arc = p->prevArc(sta);
if (p->isClock(sta))
return p;
if (prev_arc) {
const TimingRole *prev_role = prev_arc->role();
if (prev_role == TimingRole::regClkToQ()
|| prev_role == TimingRole::latchEnToQ()) {
prev_path = p->prevPath();
return prev_path;
}
else if (prev_role == TimingRole::latchDtoQ()) {
const Latches *latches = sta->latches();
Edge *prev_edge = p->prevEdge(sta);
Path *enable_path = latches->latchEnablePath(p, prev_edge);
return enable_path;
}
}
p = prev_path;
}
return nullptr;
}
PathEndDataCheck::PathEndDataCheck(DataCheck *check,
Path *data_path,
Path *data_clk_path,
Path *clk_path,
MultiCyclePath *mcp,
Crpr crpr,
bool crpr_valid) :
PathEndClkConstrainedMcp(data_path, clk_path, mcp, crpr, crpr_valid),
data_clk_path_(data_clk_path),
check_(check)
{
}
PathEnd *
PathEndDataCheck::copy() const
{
return new PathEndDataCheck(check_, path_, data_clk_path_,
clk_path_, mcp_, crpr_, crpr_valid_);
}
PathEnd::Type
PathEndDataCheck::type() const
{
return Type::data_check;
}
const char *
PathEndDataCheck::typeName() const
{
return "data_check";
}
const ClockEdge *
PathEndDataCheck::targetClkEdge(const StaState *sta) const
{
// clk_path_ can be null if data_clk_path is from an input port.
return data_clk_path_->clkEdge(sta);
}
Arrival
PathEndDataCheck::requiredTimeNoCrpr(const StaState *sta) const
{
Arrival data_clk_arrival = data_clk_path_->arrival();
float data_clk_time = data_clk_path_->clkEdge(sta)->time();
Arrival data_clk_delay = data_clk_arrival - data_clk_time;
Arrival tgt_clk_arrival = targetClkTime(sta)
+ data_clk_delay
+ targetClkUncertainty(sta)
+ targetClkMcpAdjustment(sta);
ArcDelay check_margin = margin(sta);
if (checkGenericRole(sta) == TimingRole::setup())
return tgt_clk_arrival - check_margin;
else
return tgt_clk_arrival + check_margin;
}
ArcDelay
PathEndDataCheck::margin(const StaState *sta) const
{
float margin;
bool margin_exists;
check_->margin(data_clk_path_->transition(sta),
path_->transition(sta),
path_->minMax(sta),
margin, margin_exists);
return margin;
}
const TimingRole *
PathEndDataCheck::checkRole(const StaState *sta) const
{
if (path_->minMax(sta) == MinMax::max())
return TimingRole::dataCheckSetup();
else
return TimingRole::dataCheckHold();
}
void
PathEndDataCheck::reportFull(const ReportPath *report) const
{
report->reportFull(this);
}
void
PathEndDataCheck::reportShort(const ReportPath *report) const
{
report->reportShort(this);
}
int
PathEndDataCheck::exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const
{
int cmp = PathEndClkConstrainedMcp::exceptPathCmp(path_end, sta);
if (cmp == 0) {
const PathEndDataCheck *path_end2 =
dynamic_cast(path_end);
const DataCheck *check2 = path_end2->check_;
if (check_ == check2)
return 0;
else if (check_ < check2)
return -1;
else
return 1;
}
else
return cmp;
}
////////////////////////////////////////////////////////////////
PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay,
Path *path,
const StaState *sta):
PathEndClkConstrained(path, nullptr),
path_delay_(path_delay),
check_arc_(nullptr),
check_edge_(nullptr),
output_delay_(nullptr)
{
findSrcClkArrival(sta);
}
PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay,
Path *path,
OutputDelay *output_delay,
const StaState *sta):
PathEndClkConstrained(path, nullptr),
path_delay_(path_delay),
check_arc_(nullptr),
check_edge_(nullptr),
output_delay_(output_delay)
{
findSrcClkArrival(sta);
}
PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay,
Path *path,
Path *clk_path,
TimingArc *check_arc,
Edge *check_edge,
const StaState *sta) :
PathEndClkConstrained(path, clk_path),
path_delay_(path_delay),
check_arc_(check_arc),
check_edge_(check_edge),
output_delay_(nullptr)
{
findSrcClkArrival(sta);
}
PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay,
Path *path,
Path *clk_path,
TimingArc *check_arc,
Edge *check_edge,
OutputDelay *output_delay,
Arrival src_clk_arrival,
Crpr crpr,
bool crpr_valid) :
PathEndClkConstrained(path, clk_path, crpr, crpr_valid),
path_delay_(path_delay),
check_arc_(check_arc),
check_edge_(check_edge),
output_delay_(output_delay),
src_clk_arrival_(src_clk_arrival)
{
}
PathEnd *
PathEndPathDelay::copy() const
{
return new PathEndPathDelay(path_delay_, path_, clk_path_,
check_arc_, check_edge_, output_delay_,
src_clk_arrival_, crpr_, crpr_valid_);
}
PathEnd::Type
PathEndPathDelay::type() const
{
return Type::path_delay;
}
const char *
PathEndPathDelay::typeName() const
{
return "path_delay";
}
void
PathEndPathDelay::findSrcClkArrival(const StaState *sta)
{
if (ignoreClkLatency(sta)) {
Search *search = sta->search();
src_clk_arrival_ = search->pathClkPathArrival(path_);
}
else
src_clk_arrival_ = 0.0;
}
void
PathEndPathDelay::reportFull(const ReportPath *report) const
{
report->reportFull(this);
}
void
PathEndPathDelay::reportShort(const ReportPath *report) const
{
report->reportShort(this);
}
bool
PathEndPathDelay::pathDelayMarginIsExternal() const
{
return check_arc_ == nullptr;
}
const TimingRole *
PathEndPathDelay::checkRole(const StaState *sta) const
{
if (check_edge_)
return check_edge_->role();
else if (minMax(sta) == MinMax::max())
return TimingRole::setup();
else
return TimingRole::hold();
}
ArcDelay
PathEndPathDelay::margin(const StaState *sta) const
{
if (check_arc_) {
return sta->search()->deratedDelay(check_edge_->from(sta->graph()),
check_arc_, check_edge_, false,
pathAnalysisPt(sta));
}
else if (output_delay_)
return outputDelayMargin(output_delay_, path_, sta);
else
return delay_zero;
}
float
PathEndPathDelay::sourceClkOffset(const StaState *sta) const
{
return pathDelaySrcClkOffset(path_, path_delay_, src_clk_arrival_, sta);
}
Delay
PathEnd::clkSkew(const StaState *)
{
return 0.0;
}
// Helper shared by PathEndLatchCheck.
float
PathEnd::pathDelaySrcClkOffset(const Path *path,
PathDelay *path_delay,
Arrival src_clk_arrival,
const StaState *sta)
{
float offset = 0.0;
const ClockEdge *clk_edge = path->clkEdge(sta);
if (clk_edge) {
if (ignoreClkLatency(path, path_delay, sta))
offset = -delayAsFloat(src_clk_arrival);
else
// Arrival includes src clock edge time that is not counted in the
// path delay.
offset = -clk_edge->time();
}
return offset;
}
bool
PathEnd::ignoreClkLatency(const Path *path,
PathDelay *path_delay,
const StaState *sta)
{
return path_delay->ignoreClkLatency() && !path->isClock(sta);
}
const ClockEdge *
PathEndPathDelay::targetClkEdge(const StaState *sta) const
{
if (clk_path_)
return clk_path_->clkEdge(sta);
else if (output_delay_)
return output_delay_->clkEdge();
else
return nullptr;
}
float
PathEndPathDelay::targetClkTime(const StaState *sta) const
{
const ClockEdge *tgt_clk_edge = targetClkEdge(sta);
if (tgt_clk_edge)
return tgt_clk_edge->time();
else
return 0.0;
}
Arrival
PathEndPathDelay::targetClkArrivalNoCrpr(const StaState *sta) const
{
const ClockEdge *tgt_clk_edge = targetClkEdge(sta);
if (tgt_clk_edge)
return targetClkDelay(sta)
+ targetClkUncertainty(sta);
else if (clk_path_)
return clk_path_->arrival();
else
return 0.0;
}
float
PathEndPathDelay::targetClkOffset(const StaState *) const
{
return 0.0;
}
Required
PathEndPathDelay::requiredTime(const StaState *sta) const
{
float delay = path_delay_->delay();
if (path_delay_->ignoreClkLatency()) {
Required src_offset = path_->isClock(sta)
? path_->clkEdge(sta)->time()
: src_clk_arrival_;
return src_offset + delay
+ ((minMax(sta) == MinMax::max()) ? -margin(sta) : margin(sta));
}
else {
Arrival tgt_clk_arrival = targetClkArrival(sta);
float src_clk_offset = sourceClkOffset(sta);
// Path delay includes target clk latency and timing check setup/hold
// margin or external departure at target.
return delay - src_clk_offset + tgt_clk_arrival
+ ((minMax(sta) == MinMax::max()) ? -margin(sta) : margin(sta));
}
}
bool
PathEndPathDelay::ignoreClkLatency(const StaState *sta) const
{
return PathEnd::ignoreClkLatency(path_, path_delay_, sta);
}
int
PathEndPathDelay::exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const
{
int cmp = PathEndClkConstrained::exceptPathCmp(path_end, sta);
if (cmp == 0) {
const PathEndPathDelay *path_end2 =
dynamic_cast(path_end);
PathDelay *path_delay2 = path_end2->path_delay_;
if (path_delay_ == path_delay2) {
const TimingArc *check_arc2 = path_end2->check_arc_;
if (check_arc_ == check_arc2)
return 0;
else if (check_arc_ < check_arc2)
return -1;
else
return 1;
}
else if (path_delay_ < path_delay2)
return -1;
else
return 1;
}
else
return cmp;
}
////////////////////////////////////////////////////////////////
PathEndLess::PathEndLess(const StaState *sta) :
sta_(sta)
{
}
bool
PathEndLess::operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const
{
return PathEnd::less(path_end1, path_end2, sta_);
}
bool
PathEnd::less(const PathEnd *path_end1,
const PathEnd *path_end2,
const StaState *sta)
{
return cmp(path_end1, path_end2, sta) < 0;
}
int
PathEnd::cmp(const PathEnd *path_end1,
const PathEnd *path_end2,
const StaState *sta)
{
int cmp = path_end1->isUnconstrained()
? -cmpArrival(path_end1, path_end2, sta)
: cmpSlack(path_end1, path_end2, sta);
if (cmp == 0) {
const Path *path1 = path_end1->path();
const Path *path2 = path_end2->path();
cmp = Path::cmpPinTrClk(path1, path2, sta);
if (cmp == 0) {
const Path *clk_path1 = path_end1->targetClkPath();
const Path *clk_path2 = path_end2->targetClkPath();
cmp = Path::cmpPinTrClk(clk_path1, clk_path2, sta);
if (cmp == 0) {
cmp = Path::cmpAll(path1, path2, sta);
if (cmp == 0)
cmp = Path::cmpAll(clk_path1, clk_path2, sta);
}
}
}
return cmp;
}
int
PathEnd::cmpSlack(const PathEnd *path_end1,
const PathEnd *path_end2,
const StaState *sta)
{
Slack slack1 = path_end1->slack(sta);
Slack slack2 = path_end2->slack(sta);
if (delayZero(slack1)
&& delayZero(slack2)
&& path_end1->isLatchCheck()
&& path_end2->isLatchCheck()) {
Arrival borrow1 = path_end1->borrow(sta);
Arrival borrow2 = path_end2->borrow(sta);
// Latch slack is zero if there is borrowing so break ties
// based on borrow time.
if (delayEqual(borrow1, borrow2))
return 0;
else if (delayGreater(borrow1, borrow2, sta))
return -1;
else
return 1;
}
else if (delayEqual(slack1, slack2))
return 0;
else if (delayLess(slack1, slack2, sta))
return -1;
else
return 1;
}
int
PathEnd::cmpArrival(const PathEnd *path_end1,
const PathEnd *path_end2,
const StaState *sta)
{
Arrival arrival1 = path_end1->dataArrivalTime(sta);
Arrival arrival2 = path_end2->dataArrivalTime(sta);
const MinMax *min_max = path_end1->minMax(sta);
if (delayEqual(arrival1, arrival2))
return 0;
else if (delayLess(arrival1, arrival2, min_max, sta))
return -1;
else
return 1;
}
int
PathEnd::cmpNoCrpr(const PathEnd *path_end1,
const PathEnd *path_end2,
const StaState *sta)
{
int cmp = path_end1->exceptPathCmp(path_end2, sta);
if (cmp == 0) {
const Path *path1 = path_end1->path();
const Path *path2 = path_end2->path();
return Path::cmpNoCrpr(path1, path2, sta);
}
else
return cmp;
}
////////////////////////////////////////////////////////////////
PathEndSlackLess::PathEndSlackLess(const StaState *sta) :
sta_(sta)
{
}
bool
PathEndSlackLess::operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const
{
int cmp = path_end1->isUnconstrained()
? -PathEnd::cmpArrival(path_end1, path_end2, sta_)
: PathEnd::cmpSlack(path_end1, path_end2, sta_);
return cmp < 0;
}
////////////////////////////////////////////////////////////////
PathEndNoCrprLess::PathEndNoCrprLess(const StaState *sta) :
sta_(sta)
{
}
bool
PathEndNoCrprLess::operator()(const PathEnd *path_end1,
const PathEnd *path_end2) const
{
int cmp = path_end1->exceptPathCmp(path_end2, sta_);
if (cmp == 0) {
const Path *path1 = path_end1->path();
const Path *path2 = path_end2->path();
return Path::cmpNoCrpr(path1, path2, sta_) < 0;
}
else
return cmp < 0;
}
} // namespace