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 "Machine.hh"
|
|
|
|
|
#include "Report.hh"
|
|
|
|
|
#include "Error.hh"
|
|
|
|
|
#include "StringUtil.hh"
|
|
|
|
|
#include "Units.hh"
|
|
|
|
|
#include "Fuzzy.hh"
|
|
|
|
|
#include "TimingRole.hh"
|
|
|
|
|
#include "Transition.hh"
|
|
|
|
|
#include "PortDirection.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "PortDelay.hh"
|
|
|
|
|
#include "ExceptionPath.hh"
|
|
|
|
|
#include "InputDrive.hh"
|
|
|
|
|
#include "Sdc.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "ClkInfo.hh"
|
|
|
|
|
#include "Tag.hh"
|
|
|
|
|
#include "PathVertex.hh"
|
|
|
|
|
#include "DcalcAnalysisPt.hh"
|
|
|
|
|
#include "ArcDelayCalc.hh"
|
|
|
|
|
#include "GraphDelayCalc.hh"
|
|
|
|
|
#include "Parasitics.hh"
|
|
|
|
|
#include "PathAnalysisPt.hh"
|
|
|
|
|
#include "PathGroup.hh"
|
|
|
|
|
#include "CheckMinPulseWidths.hh"
|
|
|
|
|
#include "CheckMinPeriods.hh"
|
|
|
|
|
#include "CheckMaxSkews.hh"
|
|
|
|
|
#include "PathRef.hh"
|
|
|
|
|
#include "Search.hh"
|
|
|
|
|
#include "PathExpanded.hh"
|
|
|
|
|
#include "Latches.hh"
|
|
|
|
|
#include "Corner.hh"
|
|
|
|
|
#include "Genclks.hh"
|
|
|
|
|
#include "ReportPath.hh"
|
|
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
ReportField::ReportField(const char *name,
|
|
|
|
|
const char *title,
|
|
|
|
|
int width,
|
|
|
|
|
bool left_justify,
|
|
|
|
|
Unit *unit,
|
|
|
|
|
bool enabled) :
|
|
|
|
|
name_(name),
|
|
|
|
|
title_(stringCopy(title)),
|
|
|
|
|
left_justify_(left_justify),
|
|
|
|
|
unit_(unit),
|
2018-11-09 19:04:16 +01:00
|
|
|
enabled_(enabled),
|
|
|
|
|
blank_(NULL)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
setWidth(width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReportField::~ReportField()
|
|
|
|
|
{
|
|
|
|
|
stringDelete(title_);
|
|
|
|
|
stringDelete(blank_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportField::setProperties(const char *title,
|
|
|
|
|
int width,
|
|
|
|
|
bool left_justify)
|
|
|
|
|
{
|
|
|
|
|
if (title_)
|
|
|
|
|
stringDelete(title_);
|
|
|
|
|
title_ = stringCopy(title);
|
|
|
|
|
left_justify_ = left_justify;
|
|
|
|
|
setWidth(width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportField::setWidth(int width)
|
|
|
|
|
{
|
|
|
|
|
width_ = width;
|
|
|
|
|
|
|
|
|
|
if (blank_)
|
|
|
|
|
stringDelete(blank_);
|
|
|
|
|
blank_ = new char[width + 1];
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < width; i++)
|
|
|
|
|
blank_[i] = ' ';
|
|
|
|
|
blank_[i] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportField::setEnabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
enabled_ = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
const float ReportPath::field_blank_ = -1.0;
|
|
|
|
|
|
|
|
|
|
ReportPath::ReportPath(StaState *sta) :
|
|
|
|
|
StaState(sta),
|
|
|
|
|
start_end_pt_width_(78),
|
|
|
|
|
plus_zero_(NULL),
|
|
|
|
|
minus_zero_(NULL)
|
|
|
|
|
{
|
|
|
|
|
makeFields();
|
|
|
|
|
setPathFormat(report_path_full);
|
|
|
|
|
setReportFields(false, false, false, false);
|
|
|
|
|
setDigits(2);
|
|
|
|
|
setNoSplit(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReportPath::~ReportPath()
|
|
|
|
|
{
|
|
|
|
|
delete fields_;
|
|
|
|
|
delete field_description_;
|
|
|
|
|
delete field_total_;
|
|
|
|
|
delete field_incr_;
|
|
|
|
|
delete field_capacitance_;
|
|
|
|
|
delete field_slew_;
|
|
|
|
|
delete field_fanout_;
|
|
|
|
|
delete field_edge_;
|
|
|
|
|
delete field_case_;
|
|
|
|
|
|
|
|
|
|
if (plus_zero_) {
|
|
|
|
|
stringDelete(plus_zero_);
|
|
|
|
|
stringDelete(minus_zero_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::makeFields()
|
|
|
|
|
{
|
|
|
|
|
fields_ = new ReportFieldSeq;
|
|
|
|
|
field_fanout_ = makeField("fanout", "Fanout", 10, false, NULL, true);
|
|
|
|
|
field_capacitance_ = makeField("capacitance", "Cap", 10, false,
|
|
|
|
|
units_->capacitanceUnit(), true);
|
|
|
|
|
field_slew_ = makeField("slew", "Slew", 10, false, units_->timeUnit(), true);
|
|
|
|
|
field_incr_ = makeField("incr", "Delay", 10, false, units_->timeUnit(), true);
|
|
|
|
|
field_total_ = makeField("total", "Time", 10, false, units_->timeUnit(), true);
|
|
|
|
|
field_edge_ = makeField("edge", "", 1, false, NULL, true);
|
|
|
|
|
field_case_ = makeField("case", "case", 10, false, NULL, false);
|
|
|
|
|
field_description_ = makeField("description", "Description", 36, true, NULL, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReportField *
|
|
|
|
|
ReportPath::makeField(const char *name,
|
|
|
|
|
const char *title,
|
|
|
|
|
int width,
|
|
|
|
|
bool left_justify,
|
|
|
|
|
Unit *unit,
|
|
|
|
|
bool enabled)
|
|
|
|
|
{
|
|
|
|
|
ReportField *field = new ReportField(name, title, width, left_justify,
|
|
|
|
|
unit, enabled);
|
|
|
|
|
fields_->push_back(field);
|
|
|
|
|
return field;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReportField *
|
|
|
|
|
ReportPath::findField(const char *name)
|
|
|
|
|
{
|
|
|
|
|
ReportFieldSeq::Iterator field_iter(fields_);
|
|
|
|
|
while (field_iter.hasNext()) {
|
|
|
|
|
ReportField *field = field_iter.next();
|
|
|
|
|
if (stringEq(name, field->name()))
|
|
|
|
|
return field;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::setReportFieldOrder(StringSeq *field_names)
|
|
|
|
|
{
|
|
|
|
|
// Disable all fields.
|
|
|
|
|
ReportFieldSeq::Iterator field_iter1(fields_);
|
|
|
|
|
while (field_iter1.hasNext()) {
|
|
|
|
|
ReportField *field = field_iter1.next();
|
|
|
|
|
field->setEnabled(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReportFieldSeq *next_fields = new ReportFieldSeq;
|
|
|
|
|
StringSeq::Iterator name_iter(field_names);
|
|
|
|
|
while (name_iter.hasNext()) {
|
|
|
|
|
const char *field_name = name_iter.next();
|
|
|
|
|
ReportField *field = findField(field_name);
|
|
|
|
|
next_fields->push_back(field);
|
|
|
|
|
field->setEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
// Push remaining disabled fields on the end.
|
|
|
|
|
ReportFieldSeq::Iterator field_iter2(fields_);
|
|
|
|
|
while (field_iter2.hasNext()) {
|
|
|
|
|
ReportField *field = field_iter2.next();
|
|
|
|
|
if (!field->enabled())
|
|
|
|
|
next_fields->push_back(field);
|
|
|
|
|
}
|
|
|
|
|
delete fields_;
|
|
|
|
|
fields_ = next_fields;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::setReportFields(bool report_input_pin,
|
|
|
|
|
bool report_net,
|
|
|
|
|
bool report_cap,
|
|
|
|
|
bool report_slew)
|
|
|
|
|
{
|
|
|
|
|
report_input_pin_ = report_input_pin;
|
|
|
|
|
report_net_ = report_net;
|
|
|
|
|
|
|
|
|
|
field_fanout_->setEnabled(report_net_);
|
|
|
|
|
field_capacitance_->setEnabled(report_cap);
|
|
|
|
|
field_slew_->setEnabled(report_slew);
|
|
|
|
|
// for debug
|
|
|
|
|
field_case_->setEnabled(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::setPathFormat(ReportPathFormat format)
|
|
|
|
|
{
|
|
|
|
|
format_ = format;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::setNoSplit(bool no_split)
|
|
|
|
|
{
|
|
|
|
|
no_split_ = no_split;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::setDigits(int digits)
|
|
|
|
|
{
|
|
|
|
|
digits_ = digits;
|
|
|
|
|
|
|
|
|
|
if (plus_zero_) {
|
|
|
|
|
stringDelete(plus_zero_);
|
|
|
|
|
stringDelete(minus_zero_);
|
|
|
|
|
}
|
|
|
|
|
minus_zero_ = stringPrint(digits_ + 4, "-%.*f", digits_, 0.0);
|
|
|
|
|
plus_zero_ = stringPrint(digits_ + 3, "%.*f", digits_, 0.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPathEnd(PathEnd *end)
|
|
|
|
|
{
|
|
|
|
|
string header, result;
|
|
|
|
|
switch (format_) {
|
|
|
|
|
case report_path_full:
|
|
|
|
|
case report_path_full_clock:
|
|
|
|
|
case report_path_full_clock_expanded:
|
|
|
|
|
end->reportFull(this, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n\n");
|
|
|
|
|
break;
|
|
|
|
|
case report_path_short:
|
|
|
|
|
end->reportShort(this, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n\n");
|
|
|
|
|
break;
|
|
|
|
|
case report_path_end:
|
|
|
|
|
reportEndHeader(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
reportEndLine(end, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
break;
|
|
|
|
|
case report_path_summary:
|
|
|
|
|
reportSummaryHeader(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
reportSummaryLine(end, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
break;
|
|
|
|
|
case report_path_slack_only:
|
|
|
|
|
reportSlackOnlyHeader(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
reportSlackOnly(end, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
internalError("unsupported path type");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPathEnds(PathEndSeq *ends)
|
|
|
|
|
{
|
|
|
|
|
if (!ends->empty()) {
|
|
|
|
|
string header;
|
|
|
|
|
PathEndSeq::Iterator end_iter(ends);
|
|
|
|
|
switch (format_) {
|
|
|
|
|
case report_path_full:
|
|
|
|
|
case report_path_full_clock:
|
|
|
|
|
case report_path_full_clock_expanded:
|
|
|
|
|
while (end_iter.hasNext()) {
|
|
|
|
|
PathEnd *end = end_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
end->reportFull(this, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n\n");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case report_path_short:
|
|
|
|
|
while (end_iter.hasNext()) {
|
|
|
|
|
PathEnd *end = end_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
end->reportShort(this, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n\n");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case report_path_end:
|
|
|
|
|
reportPathEndsEnd(ends);
|
|
|
|
|
break;
|
|
|
|
|
case report_path_summary:
|
|
|
|
|
reportSummaryHeader(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
while (end_iter.hasNext()) {
|
|
|
|
|
PathEnd *end = end_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
reportSummaryLine(end, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
break;
|
|
|
|
|
case report_path_slack_only:
|
|
|
|
|
reportSlackOnlyHeader(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
while (end_iter.hasNext()) {
|
|
|
|
|
PathEnd *end = end_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
reportSlackOnly(end, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
internalError("unsupported path type");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPathEndsEnd(PathEndSeq *ends)
|
|
|
|
|
{
|
|
|
|
|
PathGroup *prev_group = NULL;
|
|
|
|
|
PathEndSeq::Iterator end_iter(ends);
|
|
|
|
|
while (end_iter.hasNext()) {
|
|
|
|
|
PathEnd *end = end_iter.next();
|
|
|
|
|
PathGroup *group = search_->pathGroup(end);
|
|
|
|
|
if (group != prev_group) {
|
|
|
|
|
if (prev_group)
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
const char *setup_hold = (end->minMax(this) == MinMax::min())
|
|
|
|
|
? "min_delay/hold"
|
|
|
|
|
: "max_delay/setup";
|
|
|
|
|
report_->print("%s ('%s' group)\n\n",
|
|
|
|
|
setup_hold,
|
|
|
|
|
group->name());
|
|
|
|
|
string header;
|
|
|
|
|
reportEndHeader(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
}
|
|
|
|
|
string result;
|
|
|
|
|
reportEndLine(end, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
prev_group = group;
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPath(const Path *path)
|
|
|
|
|
{
|
|
|
|
|
string result;
|
|
|
|
|
reportPath(path, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndUnconstrained *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndUnconstrained *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartpoint(end, expanded, result);
|
|
|
|
|
reportUnclockedEndpoint(end, "internal pin", result);
|
|
|
|
|
reportGroup(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFull(const PathEndUnconstrained *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportPath(end, expanded, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("data arrival time", end->dataArrivalTimeOffset(this),
|
|
|
|
|
end->pathEarlyLate(this), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLine(result);
|
|
|
|
|
result += "(Path is unconstrained)\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndCheck *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartpoint(end, expanded, result);
|
|
|
|
|
reportEndpoint(end, result);
|
|
|
|
|
reportGroup(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFull(const PathEndCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
reportSrcPathArrival(end, expanded, result);
|
|
|
|
|
reportTgtClk(end, result);
|
|
|
|
|
reportRequired(end, checkRoleString(end), result);
|
|
|
|
|
reportSlack(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
ReportPath::checkRoleString(const PathEnd *end)
|
|
|
|
|
{
|
|
|
|
|
const char *check_role = end->checkRole(this)->asString();
|
|
|
|
|
return stringPrintTmp(strlen("library time")
|
|
|
|
|
+ strlen(check_role) + 1,
|
|
|
|
|
"library %s time",
|
|
|
|
|
check_role);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndpoint(const PathEndCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Instance *inst = network_->instance(end->vertex(this)->pin());
|
|
|
|
|
const char *inst_name = cmd_network_->pathName(inst);
|
|
|
|
|
const char *clk_name = tgtClkName(end);
|
|
|
|
|
const char *rise_fall = asRisingFalling(end->targetClkEndTrans(this));
|
|
|
|
|
const TimingRole *check_role = end->checkRole(this);
|
|
|
|
|
const TimingRole *check_generic_role = check_role->genericRole();
|
|
|
|
|
if (check_role == TimingRole::recovery()
|
|
|
|
|
|| check_role == TimingRole::removal()) {
|
|
|
|
|
const char *check_role_name = check_role->asString();
|
|
|
|
|
char *reason = stringPrintTmp(strlen(" check against -edge clock ")
|
|
|
|
|
+ strlen(check_role_name)
|
|
|
|
|
+ strlen(rise_fall)
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"%s check against %s-edge clock %s",
|
|
|
|
|
check_role_name,
|
|
|
|
|
rise_fall,
|
|
|
|
|
clk_name);
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
else if (check_generic_role == TimingRole::setup()
|
|
|
|
|
|| check_generic_role == TimingRole::hold()) {
|
|
|
|
|
LibertyCell *cell = network_->libertyCell(inst);
|
|
|
|
|
if (cell->isClockGate()) {
|
|
|
|
|
const char *reason =
|
|
|
|
|
stringPrintTmp(strlen( " clock gating-check end-point clocked by ")
|
|
|
|
|
+ strlen(rise_fall)
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"%s clock gating-check end-point clocked by %s",
|
|
|
|
|
rise_fall, clk_name);
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const char *reg_desc = clkRegLatchDesc(end);
|
|
|
|
|
const char *reason = stringPrintTmp(strlen( " clocked by ")
|
|
|
|
|
+ strlen(reg_desc)
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"%s clocked by %s",
|
|
|
|
|
reg_desc, clk_name);
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndLatchCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndLatchCheck *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartpoint(end, expanded, result);
|
|
|
|
|
reportEndpoint(end, result);
|
|
|
|
|
reportGroup(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFull(const PathEndLatchCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = end->pathEarlyLate(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
PathDelay *path_delay = end->pathDelay();
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
bool ignore_clk_latency = path_delay && path_delay->ignoreClkLatency();
|
|
|
|
|
if (ignore_clk_latency) {
|
|
|
|
|
// Based on reportSrcPath.
|
|
|
|
|
reportPathHeader(result);
|
|
|
|
|
reportPath3(end->path(), expanded, false, false, 0.0,
|
|
|
|
|
end->sourceClkOffset(this), result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportSrcPath(end, expanded, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("data arrival time", end->dataArrivalTimeOffset(this),
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
Required req_time;
|
|
|
|
|
Arrival borrow, adjusted_data_arrival, time_given_to_startpoint;
|
|
|
|
|
end->latchRequired(this, req_time, borrow, adjusted_data_arrival,
|
|
|
|
|
time_given_to_startpoint);
|
|
|
|
|
// Adjust required to requiredTimeOffset.
|
|
|
|
|
req_time += end->sourceClkOffset(this);
|
|
|
|
|
if (path_delay) {
|
|
|
|
|
float delay = path_delay->delay();
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("max_delay", delay, delay, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!ignore_clk_latency) {
|
|
|
|
|
if (reportClkPath()
|
|
|
|
|
&& isPropagated(end->targetClkPath()))
|
|
|
|
|
reportTgtClk(end, delay, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
else {
|
|
|
|
|
Delay delay1(delay);
|
|
|
|
|
reportCommonClkPessimism(end, delay1, result);
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportTgtClk(end, result);
|
|
|
|
|
|
|
|
|
|
if (borrow >= 0.0)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("time borrowed from endpoint", borrow, req_time,
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("time given to endpoint", borrow, req_time,
|
|
|
|
|
early_late, result);
|
|
|
|
|
reportLine("data required time", req_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLine(result);
|
|
|
|
|
reportSlack(end, result);
|
|
|
|
|
if (end->checkGenericRole(this) == TimingRole::setup()
|
|
|
|
|
&& !ignore_clk_latency) {
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportBorrowing(end, borrow, time_given_to_startpoint, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndpoint(const PathEndLatchCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Instance *inst = network_->instance(end->vertex(this)->pin());
|
|
|
|
|
const char *inst_name = cmd_network_->pathName(inst);
|
|
|
|
|
const char *clk_name = tgtClkName(end);
|
|
|
|
|
const char *reg_desc = latchDesc(end);
|
|
|
|
|
const char *reason = stringPrintTmp(strlen( " clocked by ")
|
|
|
|
|
+ strlen(reg_desc)
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"%s clocked by %s",
|
|
|
|
|
reg_desc, clk_name);
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::latchDesc(const PathEndLatchCheck *end)
|
|
|
|
|
{
|
|
|
|
|
// Liberty latch descriptions can have timing checks to the
|
|
|
|
|
// wrong edge of the enable, so look up the EN->Q arcs and use
|
|
|
|
|
// them to characterize the latch as positive/negative.
|
|
|
|
|
TimingArc *check_arc = end->checkArc();
|
|
|
|
|
TimingArcSet *check_set = check_arc->set();
|
|
|
|
|
LibertyCell *cell = check_set->from()->libertyCell();
|
|
|
|
|
TransRiseFall *enable_tr = cell->latchCheckEnableTrans(check_set);
|
|
|
|
|
return latchDesc(enable_tr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportBorrowing(const PathEndLatchCheck *end,
|
|
|
|
|
Arrival &borrow,
|
|
|
|
|
Arrival &time_given_to_startpoint,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Delay open_latency, latency_diff, max_borrow;
|
|
|
|
|
float nom_pulse_width, open_uncertainty, open_crpr, crpr_diff;
|
|
|
|
|
bool borrow_limit_exists;
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = EarlyLate::late();
|
2018-09-28 17:54:21 +02:00
|
|
|
end->latchBorrowInfo(this, nom_pulse_width, open_latency, latency_diff,
|
|
|
|
|
open_uncertainty, open_crpr, crpr_diff,
|
|
|
|
|
max_borrow, borrow_limit_exists);
|
|
|
|
|
result += "Time Borrowing Information\n";
|
|
|
|
|
reportDashLineTotal(result);
|
|
|
|
|
if (borrow_limit_exists)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal("user max time borrow", max_borrow, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
|
|
|
|
const char *tgt_clk_name = tgtClkName(end);
|
|
|
|
|
Arrival tgt_clk_width = end->targetClkWidth(this);
|
|
|
|
|
const Path *tgt_clk_path = end->targetClkPath();
|
|
|
|
|
if (tgt_clk_path->clkInfo(search_)->isPropagated()) {
|
|
|
|
|
const char *width_msg = stringPrintTmp(strlen(" nominal pulse width")
|
|
|
|
|
+ strlen(tgt_clk_name) + 1,
|
|
|
|
|
"%s nominal pulse width",
|
|
|
|
|
tgt_clk_name);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal(width_msg, nom_pulse_width, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!delayFuzzyZero(latency_diff))
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotalMinus("clock latency difference", latency_diff,
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const char *width_msg = stringPrintTmp(strlen(" pulse width")
|
|
|
|
|
+ strlen(tgt_clk_name) + 1,
|
|
|
|
|
"%s pulse width",
|
|
|
|
|
tgt_clk_name);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal(width_msg, tgt_clk_width, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
ArcDelay margin = end->margin(this);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotalMinus("library setup time", margin, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLineTotal(result);
|
|
|
|
|
if (!fuzzyZero(crpr_diff))
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotalMinus("CRPR difference", crpr_diff, early_late, result);
|
|
|
|
|
reportLineTotal("max time borrow", max_borrow, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
if (delayFuzzyGreater(borrow, delay_zero)
|
|
|
|
|
&& (!fuzzyZero(open_uncertainty)
|
|
|
|
|
|| !fuzzyZero(open_crpr))) {
|
|
|
|
|
reportDashLineTotal(result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal("actual time borrow", borrow, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!fuzzyZero(open_uncertainty))
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal("open edge uncertainty", open_uncertainty,
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!fuzzyZero(open_crpr))
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal("open edge CRPR", open_crpr, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLineTotal(result);
|
|
|
|
|
reportLineTotal("time given to startpoint", time_given_to_startpoint,
|
2018-11-26 18:15:52 +01:00
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal("actual time borrow", borrow, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLineTotal(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndPathDelay *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndPathDelay *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartpoint(end, expanded, result);
|
|
|
|
|
if (end->targetClk(this))
|
|
|
|
|
reportEndpoint(end, result);
|
|
|
|
|
else
|
|
|
|
|
reportUnclockedEndpoint(end, "internal path endpoint", result);
|
|
|
|
|
reportGroup(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndpoint(const PathEndPathDelay *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Instance *inst = network_->instance(end->vertex(this)->pin());
|
|
|
|
|
const char *inst_name = cmd_network_->pathName(inst);
|
|
|
|
|
const char *clk_name = tgtClkName(end);
|
|
|
|
|
const char *reg_desc = clkRegLatchDesc(end);
|
|
|
|
|
const char *reason = stringPrintTmp(strlen(" clocked by ")
|
|
|
|
|
+ strlen(reg_desc)
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"%s clocked by %s",
|
|
|
|
|
reg_desc,
|
|
|
|
|
clk_name);
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFull(const PathEndPathDelay *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = end->pathEarlyLate(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
// Based on reportSrcPathArrival.
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
PathDelay *path_delay = end->pathDelay();
|
|
|
|
|
if (path_delay->ignoreClkLatency()) {
|
|
|
|
|
// Based on reportSrcPath.
|
|
|
|
|
reportPathHeader(result);
|
|
|
|
|
reportPath3(end->path(), expanded, false, false, 0.0,
|
|
|
|
|
end->sourceClkOffset(this), result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportSrcPath(end, expanded, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("data arrival time", end->dataArrivalTimeOffset(this),
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
ArcDelay margin = end->margin(this);
|
|
|
|
|
MinMax *min_max = path_delay->minMax()->asMinMax();
|
|
|
|
|
if (min_max == MinMax::max())
|
|
|
|
|
margin = -margin;
|
|
|
|
|
|
|
|
|
|
const char *margin_what;
|
|
|
|
|
if (end->pathDelayMarginIsExternal())
|
|
|
|
|
margin_what = "output external delay";
|
|
|
|
|
else
|
|
|
|
|
margin_what = checkRoleString(end);
|
|
|
|
|
const char *min_max_str = min_max->asString();
|
|
|
|
|
char *delay_msg = stringPrintTmp(strlen(" delay")
|
|
|
|
|
+ strlen(min_max_str) + 1,
|
|
|
|
|
"%s_delay",
|
|
|
|
|
min_max_str);
|
|
|
|
|
float delay = path_delay->delay();
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(delay_msg, delay, delay, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!path_delay->ignoreClkLatency()) {
|
|
|
|
|
const Path *tgt_clk_path = end->targetClkPath();
|
|
|
|
|
if (tgt_clk_path) {
|
|
|
|
|
float delay = 0.0;
|
|
|
|
|
if (path_delay)
|
|
|
|
|
delay = path_delay->delay();
|
|
|
|
|
if (reportClkPath()
|
|
|
|
|
&& isPropagated(tgt_clk_path))
|
|
|
|
|
reportTgtClk(end, delay, result);
|
|
|
|
|
else {
|
|
|
|
|
Arrival tgt_clk_delay = end->targetClkDelay(this);
|
|
|
|
|
Arrival tgt_clk_arrival = delay + tgt_clk_delay;
|
|
|
|
|
if (!delayFuzzyZero(tgt_clk_delay))
|
|
|
|
|
reportLine(clkNetworkDelayIdealProp(isPropagated(tgt_clk_path)),
|
2018-11-26 18:15:52 +01:00
|
|
|
tgt_clk_delay, tgt_clk_arrival, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportClkUncertainty(end, tgt_clk_arrival, result);
|
|
|
|
|
reportCommonClkPessimism(end, tgt_clk_arrival, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reportRequired(end, margin_what, result);
|
|
|
|
|
reportSlack(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::isPropagated(const Path *clk_path)
|
|
|
|
|
{
|
|
|
|
|
return clk_path->clkInfo(search_)->isPropagated();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::isPropagated(const Path *clk_path,
|
|
|
|
|
Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
if (clk_path)
|
|
|
|
|
return clk_path->clkInfo(search_)->isPropagated();
|
|
|
|
|
else
|
|
|
|
|
return clk->isPropagated();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::clkNetworkDelayIdealProp(bool is_prop)
|
|
|
|
|
{
|
|
|
|
|
if (is_prop)
|
|
|
|
|
return "clock network delay (propagated)";
|
|
|
|
|
else
|
|
|
|
|
return "clock network delay (ideal)";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndOutputDelay *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndOutputDelay *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartpoint(end, expanded, result);
|
|
|
|
|
reportEndpoint(end, result);
|
|
|
|
|
reportGroup(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFull(const PathEndOutputDelay *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
reportSrcPathArrival(end, expanded, result);
|
|
|
|
|
reportTgtClk(end, result);
|
|
|
|
|
reportRequired(end, "output external delay", result);
|
|
|
|
|
reportSlack(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndpoint(const PathEndOutputDelay *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Vertex *vertex = end->vertex(this);
|
|
|
|
|
Pin *pin = vertex->pin();
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(pin);
|
|
|
|
|
Clock *tgt_clk = end->targetClk(this);
|
|
|
|
|
if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
// Pin direction is "output" even for bidirects.
|
|
|
|
|
if (tgt_clk) {
|
|
|
|
|
const char *clk_name = tgtClkName(end);
|
|
|
|
|
const char *reason = stringPrintTmp(strlen("output port clocked by ")
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"output port clocked by %s",
|
|
|
|
|
clk_name);
|
|
|
|
|
reportEndpoint(pin_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportEndpoint(pin_name, "output port", result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (tgt_clk) {
|
|
|
|
|
const char *clk_name = tgtClkName(end);
|
|
|
|
|
const char *reason = stringPrintTmp(strlen("internal path endpoint clocked by ")
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"internal path endpoint clocked by %s",
|
|
|
|
|
clk_name);
|
|
|
|
|
reportEndpoint(pin_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportEndpoint(pin_name, "internal path endpoint", result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndGatedClock *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndGatedClock *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartpoint(end, expanded, result);
|
|
|
|
|
reportEndpoint(end, result);
|
|
|
|
|
reportGroup(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFull(const PathEndGatedClock *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
reportSrcPathArrival(end, expanded, result);
|
|
|
|
|
reportTgtClk(end, result);
|
|
|
|
|
reportRequired(end, checkRoleReason(end), result);
|
|
|
|
|
reportSlack(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndpoint(const PathEndGatedClock *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Instance *inst = network_->instance(end->vertex(this)->pin());
|
|
|
|
|
const char *inst_name = cmd_network_->pathName(inst);
|
|
|
|
|
const char *clk_name = tgtClkName(end);
|
|
|
|
|
const TransRiseFall *clk_end_tr = end->targetClkEndTrans(this);
|
|
|
|
|
const TransRiseFall *clk_tr =
|
|
|
|
|
(end->minMax(this) == MinMax::max()) ? clk_end_tr : clk_end_tr->opposite();
|
|
|
|
|
const char *rise_fall = asRisingFalling(clk_tr);
|
|
|
|
|
// Note that target clock transition is ignored.
|
|
|
|
|
const char *reason = stringPrintTmp(strlen(" clock gating-check end-point clocked by ")
|
|
|
|
|
+ strlen(rise_fall)
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"%s clock gating-check end-point clocked by %s",
|
|
|
|
|
rise_fall,
|
|
|
|
|
clk_name);
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndDataCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(const PathEndDataCheck *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartpoint(end, expanded, result);
|
|
|
|
|
reportEndpoint(end, result);
|
|
|
|
|
reportGroup(end, result);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFull(const PathEndDataCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
|
|
|
|
reportShort(end, expanded, result);
|
|
|
|
|
reportSrcPathArrival(end, expanded, result);
|
|
|
|
|
|
|
|
|
|
// Capture/target clock path reporting resembles both source (reportSrcPath)
|
|
|
|
|
// and target (reportTgtClk) clocks.
|
|
|
|
|
// It is like a source because it can be a non-clock path.
|
|
|
|
|
// It is like a target because crpr and uncertainty are reported.
|
|
|
|
|
// It is always propagated, even if the clock is ideal.
|
|
|
|
|
reportTgtClk(end, 0.0, true, result);
|
|
|
|
|
const PathVertex *data_clk_path = end->dataClkPath();
|
|
|
|
|
if (!data_clk_path->isClock(this)) {
|
|
|
|
|
// Report the path from the clk network to the data check.
|
|
|
|
|
PathExpanded clk_expanded(data_clk_path, this);
|
|
|
|
|
float src_offset = end->sourceClkOffset(this);
|
2018-11-26 18:15:52 +01:00
|
|
|
Delay clk_delay = end->targetClkDelay(this);
|
|
|
|
|
Arrival clk_arrival = end->targetClkArrival(this);
|
|
|
|
|
float offset = delayAsFloat(clk_arrival - clk_delay + src_offset);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportPath5(data_clk_path, clk_expanded, clk_expanded.startIndex(),
|
|
|
|
|
clk_expanded.size() - 1,
|
|
|
|
|
data_clk_path->clkInfo(search_)->isPropagated(), false,
|
|
|
|
|
// Delay to startpoint is already included.
|
|
|
|
|
clk_arrival + src_offset, offset, result);
|
|
|
|
|
}
|
|
|
|
|
reportRequired(end, checkRoleReason(end), result);
|
|
|
|
|
reportSlack(end, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndpoint(const PathEndDataCheck *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Instance *inst = network_->instance(end->vertex(this)->pin());
|
|
|
|
|
const char *inst_name = cmd_network_->pathName(inst);
|
|
|
|
|
const char *tgt_clk_tr = asRisingFalling(end->dataClkPath()->transition(this));
|
|
|
|
|
const char *tgt_clk_name = end->targetClk(this)->name();
|
|
|
|
|
char *reason = stringPrintTmp(strlen(" edge-triggered data to data check clocked by ")
|
|
|
|
|
+ strlen(tgt_clk_tr)
|
|
|
|
|
+ strlen(tgt_clk_name) + 1,
|
|
|
|
|
"%s edge-triggered data to data check clocked by %s",
|
|
|
|
|
tgt_clk_tr,
|
|
|
|
|
tgt_clk_name);
|
|
|
|
|
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndHeader(string &result)
|
|
|
|
|
{
|
|
|
|
|
// Line one.
|
|
|
|
|
reportDescription("", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Required ", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Actual ", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
// Line two.
|
|
|
|
|
reportDescription("Endpoint", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Path Delay", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Path Delay", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Slack", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportDashLine(field_description_->width() + field_total_->width() * 3 + 3,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndLine(PathEnd *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = end->pathEarlyLate(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDescription(pathEndpoint(end), result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(end->requiredTimeOffset(this), early_late, result);
|
|
|
|
|
reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
Slack slack = end->slack(this);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(slack, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
result += (slack >= 0.0) ? " (MET)" : " (VIOLATED)";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSummaryHeader(string &result)
|
|
|
|
|
{
|
|
|
|
|
reportDescription("Startpoint", result);
|
|
|
|
|
reportDescription("Endpoint", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Slack", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportDashLine(field_description_->width() * 2 + field_total_->width() + 1,
|
|
|
|
|
result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSummaryLine(PathEnd *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathExpanded expanded(end->path(), this);
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = end->pathEarlyLate(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDescription(pathStartpoint(end, expanded), result);
|
|
|
|
|
reportDescription(pathEndpoint(end), result);
|
|
|
|
|
if (end->isUnconstrained())
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(end->slack(this), early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::pathStartpoint(PathEnd *end,
|
|
|
|
|
PathExpanded &expanded)
|
|
|
|
|
{
|
|
|
|
|
PathRef *start = expanded.startPath();
|
|
|
|
|
Pin *pin = start->pin(graph_);
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(pin);
|
|
|
|
|
if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
PortDirection *dir = network_->direction(pin);
|
|
|
|
|
const char *dir_str = dir->name();
|
|
|
|
|
return stringPrintTmp(strlen(pin_name) + strlen(dir_str) + 4,
|
|
|
|
|
"%s (%s)",
|
|
|
|
|
pin_name, dir_str);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Instance *inst = network_->instance(end->vertex(this)->pin());
|
|
|
|
|
const char *cell_name = cmd_network_->name(network_->cell(inst));
|
|
|
|
|
return stringPrintTmp(strlen(pin_name) + strlen(cell_name) + 4,
|
|
|
|
|
"%s (%s)",
|
|
|
|
|
pin_name,
|
|
|
|
|
cell_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::pathEndpoint(PathEnd *end)
|
|
|
|
|
{
|
|
|
|
|
Pin *pin = end->vertex(this)->pin();
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(pin);
|
|
|
|
|
if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
PortDirection *dir = network_->direction(pin);
|
|
|
|
|
const char *dir_str = dir->name();
|
|
|
|
|
return stringPrintTmp(strlen(pin_name) + strlen(dir_str) + 4,
|
|
|
|
|
"%s (%s)",
|
|
|
|
|
pin_name, dir_str);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Instance *inst = network_->instance(end->vertex(this)->pin());
|
|
|
|
|
const char *cell_name = cmd_network_->name(network_->cell(inst));
|
|
|
|
|
return stringPrintTmp(strlen(pin_name) + strlen(cell_name) + 4,
|
|
|
|
|
"%s (%s)",
|
|
|
|
|
pin_name,
|
|
|
|
|
cell_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlackOnlyHeader(string &result)
|
|
|
|
|
{
|
|
|
|
|
reportDescription("Group", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Slack", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportDashLine(field_description_->width() + field_total_->width() + 1,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlackOnly(PathEnd *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = end->pathEarlyLate(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDescription(search_->pathGroup(end)->name(), result);
|
|
|
|
|
if (end->isUnconstrained())
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(end->slack(this), early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportMpwCheck(MinPulseWidthCheck *check,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
|
|
|
|
if (verbose) {
|
|
|
|
|
string result;
|
|
|
|
|
reportVerbose(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
string header;
|
|
|
|
|
reportMpwHeaderShort(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
string result;
|
|
|
|
|
reportShort(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportMpwChecks(MinPulseWidthCheckSeq *checks,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
|
|
|
|
if (!checks->empty()) {
|
|
|
|
|
if (verbose) {
|
|
|
|
|
MinPulseWidthCheckSeq::Iterator check_iter(checks);
|
|
|
|
|
while (check_iter.hasNext()) {
|
|
|
|
|
MinPulseWidthCheck *check = check_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
reportVerbose(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
string header;
|
|
|
|
|
reportMpwHeaderShort(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
MinPulseWidthCheckSeq::Iterator check_iter(checks);
|
|
|
|
|
while (check_iter.hasNext()) {
|
|
|
|
|
MinPulseWidthCheck *check = check_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
reportShort(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportMpwHeaderShort(string &result)
|
|
|
|
|
{
|
|
|
|
|
reportDescription("", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Required", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Actual", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportDescription("Pin", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("width ", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("width", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Slack", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportDashLine(field_description_->width() + field_total_->width() * 3 + 3,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(MinPulseWidthCheck *check,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(check->pin(this));
|
|
|
|
|
const char *hi_low = mpwCheckHiLow(check);
|
|
|
|
|
char *what = stringPrintTmp(strlen(" ()")
|
|
|
|
|
+ strlen(pin_name)
|
|
|
|
|
+ strlen(hi_low) + 1,
|
|
|
|
|
"%s (%s)",
|
|
|
|
|
pin_name,
|
|
|
|
|
hi_low);
|
|
|
|
|
reportDescription(what, result);
|
|
|
|
|
reportSpaceFieldTime(check->minWidth(this), result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(check->width(this), EarlyLate::late(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
Slack slack = check->slack(this);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(slack, EarlyLate::early(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
result += (slack >= 0.0) ? " (MET)" : " (VIOLATED)";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportVerbose(MinPulseWidthCheck *check,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(check->pin(this));
|
|
|
|
|
result += "Pin: ";
|
|
|
|
|
result += pin_name;
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
result += "Check: sequential_clock_pulse_width\n";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportPathHeader(result);
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *open_el = EarlyLate::late();
|
2018-09-28 17:54:21 +02:00
|
|
|
ClockEdge *open_clk_edge = check->openClkEdge(this);
|
|
|
|
|
Clock *open_clk = open_clk_edge->clock();
|
|
|
|
|
const char *open_clk_name = open_clk->name();
|
|
|
|
|
const char *open_rise_fall = asRiseFall(open_clk_edge->transition());
|
|
|
|
|
float open_clk_time = open_clk_edge->time();
|
|
|
|
|
const char *open_clk_msg = stringPrintTmp(strlen("clock ( edge)")
|
|
|
|
|
+ strlen(open_clk_name)
|
|
|
|
|
+ strlen(open_rise_fall) + 1,
|
|
|
|
|
"clock %s (%s edge)",
|
|
|
|
|
open_clk_name,
|
|
|
|
|
open_rise_fall);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(open_clk_msg, open_clk_time, open_clk_time,
|
|
|
|
|
open_el, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
Arrival open_arrival = check->openArrival(this);
|
|
|
|
|
bool is_prop = isPropagated(check->openPath());
|
|
|
|
|
const char *clk_ideal_prop = clkNetworkDelayIdealProp(is_prop);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(clk_ideal_prop, check->openDelay(this), open_arrival,
|
|
|
|
|
open_el, result);
|
|
|
|
|
reportLine(pin_name, delay_zero, open_arrival, open_el, result);
|
|
|
|
|
reportLine("open edge arrival time", open_arrival, open_el, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *close_el = EarlyLate::late();
|
2018-09-28 17:54:21 +02:00
|
|
|
ClockEdge *close_clk_edge = check->closeClkEdge(this);
|
|
|
|
|
Clock *close_clk = close_clk_edge->clock();
|
|
|
|
|
const char *close_clk_name = close_clk->name();
|
|
|
|
|
const char *close_rise_fall = asRiseFall(close_clk_edge->transition());
|
|
|
|
|
float close_offset = check->closeOffset(this);
|
|
|
|
|
float close_clk_time = close_clk_edge->time() + close_offset;
|
|
|
|
|
const char *close_clk_msg = stringPrintTmp(strlen("clock ( edge)")
|
|
|
|
|
+ strlen(close_clk_name)
|
|
|
|
|
+ strlen(close_rise_fall) + 1,
|
|
|
|
|
"clock %s (%s edge)",
|
|
|
|
|
close_clk_name,
|
|
|
|
|
close_rise_fall);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(close_clk_msg, close_clk_time, close_clk_time, close_el, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
Arrival close_arrival = check->closeArrival(this) + close_offset;
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(clk_ideal_prop, check->closeDelay(this), close_arrival,
|
|
|
|
|
close_el, result);
|
|
|
|
|
reportLine(pin_name, delay_zero, close_arrival, close_el, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
if (sdc_->crprEnabled()) {
|
|
|
|
|
float pessimism = check->commonClkPessimism(this);
|
|
|
|
|
close_arrival += pessimism;
|
|
|
|
|
reportLine("clock reconvergence pessimism", pessimism, close_arrival,
|
2018-11-26 18:15:52 +01:00
|
|
|
close_el, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("close edge arrival time", close_arrival, close_el, result);
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLine(result);
|
|
|
|
|
float min_width = check->minWidth(this);
|
|
|
|
|
const char *hi_low = mpwCheckHiLow(check);
|
|
|
|
|
const char *rpw_msg = stringPrintTmp(strlen("required pulse width ()")
|
|
|
|
|
+ strlen(hi_low) + 1,
|
|
|
|
|
"required pulse width (%s)",
|
|
|
|
|
hi_low);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(rpw_msg, min_width, EarlyLate::early(), result);
|
|
|
|
|
reportLine("actual pulse width", check->width(this),
|
|
|
|
|
EarlyLate::early(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLine(result);
|
|
|
|
|
reportSlack(check->slack(this), result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::mpwCheckHiLow(MinPulseWidthCheck *check)
|
|
|
|
|
{
|
|
|
|
|
if (check->openTransition(this) == TransRiseFall::rise())
|
|
|
|
|
return "high";
|
|
|
|
|
else
|
|
|
|
|
return "low";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportCheck(MinPeriodCheck *check,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
|
|
|
|
if (verbose) {
|
|
|
|
|
string result;
|
|
|
|
|
reportVerbose(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
string header;
|
|
|
|
|
reportPeriodHeaderShort(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
string result;
|
|
|
|
|
reportShort(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportChecks(MinPeriodCheckSeq *checks,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
|
|
|
|
if (!checks->empty()) {
|
|
|
|
|
if (verbose) {
|
|
|
|
|
MinPeriodCheckSeq::Iterator check_iter(checks);
|
|
|
|
|
while (check_iter.hasNext()) {
|
|
|
|
|
MinPeriodCheck *check = check_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
reportVerbose(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
string header;
|
|
|
|
|
reportPeriodHeaderShort(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
MinPeriodCheckSeq::Iterator check_iter(checks);
|
|
|
|
|
while (check_iter.hasNext()) {
|
|
|
|
|
MinPeriodCheck *check = check_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
reportShort(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPeriodHeaderShort(string &result)
|
|
|
|
|
{
|
|
|
|
|
reportDescription("", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Min ", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportDescription("Pin", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Period", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Period", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Slack", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportDashLine(field_description_->width() + field_total_->width() * 3 + 3,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(MinPeriodCheck *check,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(check->pin());
|
|
|
|
|
reportDescription(pin_name, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(check->period(), EarlyLate::early(), result);
|
|
|
|
|
reportSpaceFieldDelay(check->minPeriod(this), EarlyLate::early(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
Slack slack = check->slack(this);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(slack, EarlyLate::early(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
result += (slack >= 0.0) ? " (MET)" : " (VIOLATED)";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportVerbose(MinPeriodCheck *check, string &result)
|
|
|
|
|
{
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(check->pin());
|
|
|
|
|
result += "Pin: ";
|
|
|
|
|
result += pin_name;
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("Period", check->period(), EarlyLate::early(), result);
|
|
|
|
|
reportLine("min_period", -check->minPeriod(this),
|
|
|
|
|
EarlyLate::early(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLine(result);
|
|
|
|
|
reportSlack(check->slack(this), result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportCheck(MaxSkewCheck *check,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
|
|
|
|
if (verbose) {
|
|
|
|
|
string result;
|
|
|
|
|
reportVerbose(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
string header;
|
|
|
|
|
reportMaxSkewHeaderShort(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
string result;
|
|
|
|
|
reportShort(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportChecks(MaxSkewCheckSeq *checks,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
|
|
|
|
if (!checks->empty()) {
|
|
|
|
|
if (verbose) {
|
|
|
|
|
MaxSkewCheckSeq::Iterator check_iter(checks);
|
|
|
|
|
while (check_iter.hasNext()) {
|
|
|
|
|
MaxSkewCheck *check = check_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
reportVerbose(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
string header;
|
|
|
|
|
reportMaxSkewHeaderShort(header);
|
|
|
|
|
report_->print(header);
|
|
|
|
|
MaxSkewCheckSeq::Iterator check_iter(checks);
|
|
|
|
|
while (check_iter.hasNext()) {
|
|
|
|
|
MaxSkewCheck *check = check_iter.next();
|
|
|
|
|
string result;
|
|
|
|
|
reportShort(check, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
report_->print("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportMaxSkewHeaderShort(string &result)
|
|
|
|
|
{
|
|
|
|
|
reportDescription("", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Required", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Actual", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportDescription("Pin", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Skew", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Skew", field_total_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Slack", field_total_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportDashLine(field_description_->width() + field_total_->width() * 3 + 3,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportShort(MaxSkewCheck *check,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Pin *clk_pin = check->clkPin(this);
|
|
|
|
|
const char *clk_pin_name = network_->pathName(clk_pin);
|
|
|
|
|
TimingArc *check_arc = check->checkArc();
|
|
|
|
|
const char *what = stringPrintTmp(strlen(clk_pin_name)
|
|
|
|
|
+ strlen(" (r->r)") + 1,
|
|
|
|
|
"%s (%s->%s)",
|
|
|
|
|
clk_pin_name,
|
|
|
|
|
check_arc->fromTrans()->asString(),
|
|
|
|
|
check_arc->toTrans()->asString());
|
|
|
|
|
reportDescription(what, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(check->maxSkew(this), EarlyLate::early(), result);
|
|
|
|
|
reportSpaceFieldDelay(check->skew(this), EarlyLate::early(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
Slack slack = check->slack(this);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(slack, EarlyLate::early(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
result += (slack >= 0.0) ? " (MET)" : " (VIOLATED)";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportVerbose(MaxSkewCheck *check,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const char *clk_pin_name = cmd_network_->pathName(check->clkPin(this));
|
|
|
|
|
result += "Constrained Pin: ";
|
|
|
|
|
result += clk_pin_name;
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
const char *ref_pin_name = cmd_network_->pathName(check->refPin(this));
|
|
|
|
|
result += "Reference Pin: ";
|
|
|
|
|
result += ref_pin_name;
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
result += "Check: max_skew";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportPathHeader(result);
|
|
|
|
|
reportSkewClkPath("reference pin arrival time", check->refPath(), result);
|
|
|
|
|
reportSkewClkPath("constrained pin arrival time", check->clkPath(), result);
|
|
|
|
|
|
|
|
|
|
reportDashLine(result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("allowable skew", check->maxSkew(this),
|
|
|
|
|
EarlyLate::early(), result);
|
|
|
|
|
reportLine("actual skew", check->skew(this), EarlyLate::late(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLine(result);
|
|
|
|
|
reportSlack(check->slack(this), result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Based on reportTgtClk.
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSkewClkPath(const char *arrival_msg,
|
|
|
|
|
const PathVertex *clk_path,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
ClockEdge *clk_edge = clk_path->clkEdge(this);
|
|
|
|
|
Clock *clk = clk_edge->clock();
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = clk_path->minMax(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
const TransRiseFall *clk_tr = clk_edge->transition();
|
|
|
|
|
const TransRiseFall *clk_end_tr = clk_path->transition(this);
|
|
|
|
|
const char *clk_name = (clk_end_tr == clk_tr)
|
|
|
|
|
? clk->name()
|
|
|
|
|
: clkNameInverted(clk->name());
|
|
|
|
|
float clk_time = clk_edge->time();
|
|
|
|
|
const Arrival &clk_arrival = search_->clkPathArrival(clk_path);
|
|
|
|
|
Arrival clk_delay = clk_arrival - clk_time;
|
|
|
|
|
PathAnalysisPt *path_ap = clk_path->pathAnalysisPt(this);
|
|
|
|
|
const MinMax *min_max = path_ap->pathMinMax();
|
|
|
|
|
Vertex *clk_vertex = clk_path->vertex(this);
|
|
|
|
|
reportClkLine(clk, clk_name, clk_end_tr, clk_time, min_max, result);
|
|
|
|
|
bool is_prop = isPropagated(clk_path);
|
|
|
|
|
if (is_prop && reportClkPath()) {
|
|
|
|
|
const EarlyLate *early_late = TimingRole::skew()->tgtClkEarlyLate();
|
|
|
|
|
if (reportGenClkSrcPath(clk_path, clk, clk_tr, min_max, early_late))
|
|
|
|
|
reportGenClkSrcAndPath(clk_path, clk, clk_tr, early_late, path_ap,
|
|
|
|
|
0.0, 0.0, false, result);
|
|
|
|
|
else {
|
|
|
|
|
Arrival insertion, latency;
|
|
|
|
|
PathEnd::checkTgtClkDelay(clk_path, clk_edge, TimingRole::skew(), this,
|
|
|
|
|
insertion, latency);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportClkSrcLatency(insertion, clk_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
PathExpanded clk_expanded(clk_path, this);
|
|
|
|
|
reportPath2(clk_path, clk_expanded, false, 0.0, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival,
|
2018-11-26 18:15:52 +01:00
|
|
|
early_late, result);
|
|
|
|
|
reportLine(descriptionField(clk_vertex), clk_arrival, early_late,
|
|
|
|
|
clk_end_tr, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(arrival_msg, search_->clkPathArrival(clk_path),
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlewLimitShortHeader()
|
|
|
|
|
{
|
|
|
|
|
string result;
|
|
|
|
|
reportSlewLimitShortHeader(result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlewLimitShortHeader(string &result)
|
|
|
|
|
{
|
|
|
|
|
reportDescription("Pin", result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Limit", field_slew_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Trans", field_slew_, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField("Slack", field_slew_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportDashLine(field_description_->width() + field_slew_->width() * 3 + 3,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlewLimitShort(Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
Slew slew,
|
|
|
|
|
float limit,
|
|
|
|
|
float slack)
|
|
|
|
|
{
|
|
|
|
|
string result;
|
|
|
|
|
reportSlewLimitShort(pin, tr, slew, limit, slack, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlewLimitShort(Pin *pin,
|
|
|
|
|
const TransRiseFall *,
|
|
|
|
|
Slew slew,
|
|
|
|
|
float limit,
|
|
|
|
|
float slack,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(pin);
|
|
|
|
|
reportDescription(pin_name, result);
|
|
|
|
|
reportSpaceFieldTime(limit, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportSpaceFieldDelay(slew, EarlyLate::late(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportSpaceFieldTime(slack, result);
|
|
|
|
|
result += (slack >= 0.0) ? " (MET)" : " (VIOLATED)";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlewLimitVerbose(Pin *pin,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
Slew slew,
|
|
|
|
|
float limit,
|
|
|
|
|
float slack,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
string result;
|
|
|
|
|
reportSlewLimitVerbose(pin, corner, tr, slew, limit, slack, min_max, result);
|
|
|
|
|
report_->print(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlewLimitVerbose(Pin *pin,
|
|
|
|
|
const Corner *,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
Slew slew,
|
|
|
|
|
float limit,
|
|
|
|
|
float slack,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
result += "Pin ";
|
|
|
|
|
result += cmd_network_->pathName(pin);
|
|
|
|
|
result += ' ';
|
|
|
|
|
result += tr->shortName();
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
result += min_max->asString();
|
|
|
|
|
result += "_transition ";
|
|
|
|
|
reportSpaceFieldTime(limit, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
result += "transition_time ";
|
|
|
|
|
reportField(delayAsFloat(slew), field_slew_, result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportDashLine(strlen("transition_time") + field_slew_->width() + 1,
|
|
|
|
|
result);
|
|
|
|
|
|
|
|
|
|
result += "Slack ";
|
|
|
|
|
reportSpaceFieldTime(slack, result);
|
|
|
|
|
result += (slack >= 0.0) ? " (MET)" : " (VIOLATED)";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportStartpoint(const PathEnd *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const Path *path = end->path();
|
|
|
|
|
PathRef *start = expanded.startPath();
|
|
|
|
|
TimingArc *prev_arc = expanded.startPrevArc();
|
|
|
|
|
Edge *prev_edge = start->prevEdge(prev_arc, this);
|
|
|
|
|
Pin *pin = start->pin(graph_);
|
|
|
|
|
ClockEdge *clk_edge = path->clkEdge(this);
|
|
|
|
|
Clock *clk = path->clock(search_);
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(pin);
|
|
|
|
|
if (pathFromClkPin(path, pin)) {
|
|
|
|
|
const char *clk_name = clk->name();
|
|
|
|
|
const char *reason = stringPrintTmp(strlen("clock source ''")
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"clock source '%s'", clk_name);
|
|
|
|
|
reportStartpoint(pin_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
else if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
if (clk
|
|
|
|
|
&& clk != sdc_->defaultArrivalClock()) {
|
|
|
|
|
const char *clk_name = clk->name();
|
|
|
|
|
// Pin direction is "input" even for bidirects.
|
|
|
|
|
const char *reason=stringPrintTmp(strlen("input port clocked by ")
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"input port clocked by %s",
|
|
|
|
|
clk_name);
|
|
|
|
|
reportStartpoint(pin_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportStartpoint(pin_name, "input port", result);
|
|
|
|
|
}
|
|
|
|
|
else if (network_->isLeaf(pin) && prev_arc) {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
const char *inst_name = cmd_network_->pathName(inst);
|
|
|
|
|
if (clk_edge) {
|
|
|
|
|
const char *clk_name = clk->name();
|
|
|
|
|
const TransRiseFall *clk_tr = clk_edge->transition();
|
|
|
|
|
const TransRiseFall *clk_end_tr = clk_tr;
|
|
|
|
|
PathRef clk_path;
|
|
|
|
|
expanded.clkPath(clk_path);
|
|
|
|
|
if (!clk_path.isNull()) {
|
|
|
|
|
clk_end_tr = clk_path.transition(this);
|
|
|
|
|
if (clk_end_tr != clk_tr)
|
|
|
|
|
clk_name = clkNameInverted(clk_name);
|
|
|
|
|
}
|
|
|
|
|
const char *reg_desc = edgeRegLatchDesc(prev_edge, prev_arc);
|
|
|
|
|
const char *reason = stringPrintTmp(strlen(" clocked by ")
|
|
|
|
|
+ strlen(reg_desc)
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"%s clocked by %s",
|
|
|
|
|
reg_desc, clk_name);
|
|
|
|
|
reportStartpoint(inst_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const char *reg_desc = edgeRegLatchDesc(prev_edge, prev_arc);
|
|
|
|
|
reportStartpoint(inst_name, reg_desc, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (network_->isLeaf(pin)) {
|
|
|
|
|
if (clk_edge) {
|
|
|
|
|
Clock *clk = clk_edge->clock();
|
|
|
|
|
if (clk != sdc_->defaultArrivalClock()) {
|
|
|
|
|
const char *clk_name = clk->name();
|
|
|
|
|
const char *reason =
|
|
|
|
|
stringPrintTmp(strlen("internal path startpoint clocked by ")
|
|
|
|
|
+ strlen(clk_name) + 1,
|
|
|
|
|
"internal path startpoint clocked by %s",
|
|
|
|
|
clk_name);
|
|
|
|
|
reportStartpoint(pin_name, reason, result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportStartpoint(pin_name, "internal path startpoint", result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportStartpoint(pin_name, "internal pin", result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportStartpoint(pin_name, "", result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::pathFromClkPin(PathExpanded &expanded)
|
|
|
|
|
{
|
|
|
|
|
PathRef *start = expanded.startPath();
|
|
|
|
|
PathRef *end = expanded.endPath();
|
|
|
|
|
const Pin *start_pin = start->pin(graph_);
|
|
|
|
|
return pathFromClkPin(end, start_pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::pathFromClkPin(const Path *path,
|
|
|
|
|
const Pin *start_pin)
|
|
|
|
|
{
|
|
|
|
|
Clock *clk = path->clock(search_);
|
|
|
|
|
return clk
|
|
|
|
|
&& clk->vertexPins()
|
|
|
|
|
&& clk->vertexPins()->hasKey(const_cast<Pin*>(start_pin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportStartpoint(const char *start,
|
|
|
|
|
const char *reason,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartEndPoint(start, reason, "Startpoint", result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportUnclockedEndpoint(const PathEnd *end,
|
|
|
|
|
const char *default_reason,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Vertex *vertex = end->vertex(this);
|
|
|
|
|
Pin *pin = vertex->pin();
|
|
|
|
|
if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
// Pin direction is "output" even for bidirects.
|
|
|
|
|
reportEndpoint(cmd_network_->pathName(pin), "output port", result);
|
|
|
|
|
}
|
|
|
|
|
else if (network_->isLeaf(pin)) {
|
|
|
|
|
VertexInEdgeIterator edge_iter(vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (edge->role()->genericRole() == TimingRole::setup()) {
|
|
|
|
|
Vertex *clk_vertex = edge->from(graph_);
|
|
|
|
|
VertexOutEdgeIterator clk_edge_iter(clk_vertex, graph_);
|
|
|
|
|
while (clk_edge_iter.hasNext()) {
|
|
|
|
|
Edge *clk_edge = clk_edge_iter.next();
|
|
|
|
|
if (clk_edge->role() == TimingRole::regClkToQ()) {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
const char *inst_name = cmd_network_->pathName(inst);
|
|
|
|
|
const char *reason = regDesc(clk_edge->timingArcSet()->isRisingFallingEdge());
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (clk_edge->role() == TimingRole::latchEnToQ()) {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
const char *inst_name = cmd_network_->pathName(inst);
|
|
|
|
|
const char *reason = latchDesc(clk_edge->timingArcSet()->isRisingFallingEdge());
|
|
|
|
|
reportEndpoint(inst_name, reason, result);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reportEndpoint(cmd_network_->pathName(pin), default_reason, result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportEndpoint(cmd_network_->pathName(pin), "", result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndpoint(const char *end, const char *reason, string &result)
|
|
|
|
|
{
|
|
|
|
|
reportStartEndPoint(end, reason, "Endpoint", result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportStartEndPoint(const char *pt,
|
|
|
|
|
const char *reason,
|
|
|
|
|
const char *key,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
// Account for punctuation in the line.
|
|
|
|
|
int line_len = strlen(key) + 2 + strlen(pt) + 2 + strlen(reason) + 1;
|
|
|
|
|
if (!no_split_
|
|
|
|
|
&& line_len > start_end_pt_width_) {
|
|
|
|
|
result += key;
|
|
|
|
|
result += ": ";
|
|
|
|
|
result += pt;
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
2018-11-09 19:04:16 +01:00
|
|
|
for (unsigned i = 0; i < strlen(key); i++)
|
2018-09-28 17:54:21 +02:00
|
|
|
result += ' ';
|
|
|
|
|
|
|
|
|
|
result += " (";
|
|
|
|
|
result += reason;
|
|
|
|
|
result += ")\n";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
result += key;
|
|
|
|
|
result += ": ";
|
|
|
|
|
result += pt;
|
|
|
|
|
result += " (";
|
|
|
|
|
result += reason;
|
|
|
|
|
result += ")\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportGroup(const PathEnd *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
result += "Path Group: ";
|
|
|
|
|
result += search_->pathGroup(end)->name();
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
result += "Path Type: ";
|
|
|
|
|
result += end->minMax(this)->asString();
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
if (corners_->multiCorner()) {
|
|
|
|
|
result += "Corner: ";
|
|
|
|
|
result += end->pathAnalysisPt(this)->corner()->name();
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::checkRoleReason(const PathEnd *end)
|
|
|
|
|
{
|
|
|
|
|
const char *setup_hold = end->checkRole(this)->asString();
|
|
|
|
|
return stringPrintTmp(strlen(" time")
|
|
|
|
|
+ strlen(setup_hold) + 1,
|
|
|
|
|
"%s time",
|
|
|
|
|
setup_hold);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::tgtClkName(const PathEnd *end)
|
|
|
|
|
{
|
|
|
|
|
ClockEdge *tgt_clk_edge = end->targetClkEdge(this);
|
|
|
|
|
const Clock *tgt_clk = tgt_clk_edge->clock();
|
|
|
|
|
const TransRiseFall *clk_tr = tgt_clk_edge->transition();
|
|
|
|
|
const TransRiseFall *clk_end_tr = end->targetClkEndTrans(this);
|
|
|
|
|
const char *clk_name = tgt_clk->name();
|
|
|
|
|
if (clk_end_tr == clk_tr)
|
|
|
|
|
return clk_name;
|
|
|
|
|
else
|
|
|
|
|
return clkNameInverted(clk_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::clkNameInverted(const char *clk_name)
|
|
|
|
|
{
|
|
|
|
|
return stringPrintTmp(strlen(clk_name) + 2, "%s'", clk_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::clkRegLatchDesc(const PathEnd *end)
|
|
|
|
|
{
|
|
|
|
|
// Goofy libraries can have registers with both rising and falling
|
|
|
|
|
// clk->q timing arcs. Try and match the timing check transition.
|
|
|
|
|
const TransRiseFall *check_clk_tr=end->checkArc()->fromTrans()->asRiseFall();
|
|
|
|
|
TimingArcSet *clk_set = NULL;
|
|
|
|
|
TimingArcSet *clk_tr_set = NULL;
|
|
|
|
|
Vertex *tgt_clk_vertex = end->targetClkPath()->vertex(this);
|
|
|
|
|
VertexOutEdgeIterator iter(tgt_clk_vertex, graph_);
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
Edge *edge = iter.next();
|
|
|
|
|
TimingArcSet *arc_set = edge->timingArcSet();
|
|
|
|
|
TimingRole *role = arc_set->role();
|
|
|
|
|
if (role == TimingRole::regClkToQ()
|
|
|
|
|
|| role == TimingRole::latchEnToQ()) {
|
|
|
|
|
TransRiseFall *arc_tr = arc_set->isRisingFallingEdge();
|
|
|
|
|
clk_set = arc_set;
|
|
|
|
|
if (arc_tr == check_clk_tr)
|
|
|
|
|
clk_tr_set = arc_set;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (clk_tr_set)
|
|
|
|
|
return checkRegLatchDesc(clk_tr_set->role(),
|
|
|
|
|
clk_tr_set->isRisingFallingEdge());
|
|
|
|
|
else if (clk_set)
|
|
|
|
|
return checkRegLatchDesc(clk_set->role(),
|
|
|
|
|
clk_set->isRisingFallingEdge());
|
|
|
|
|
else
|
|
|
|
|
return checkRegLatchDesc(TimingRole::regClkToQ(), check_clk_tr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSrcPathArrival(const PathEnd *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
reportSrcPath(end, expanded, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("data arrival time", end->dataArrivalTimeOffset(this),
|
|
|
|
|
end->pathEarlyLate(this), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSrcPath(const PathEnd *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportPathHeader(result);
|
|
|
|
|
float src_clk_offset = end->sourceClkOffset(this);
|
|
|
|
|
Arrival src_clk_insertion = end->sourceClkInsertionDelay(this);
|
|
|
|
|
Arrival src_clk_latency = end->sourceClkLatency(this);
|
|
|
|
|
const Path *path = end->path();
|
|
|
|
|
reportSrcClkAndPath(path, expanded, src_clk_offset, src_clk_insertion,
|
|
|
|
|
src_clk_latency, end->isPathDelay(), result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSrcClkAndPath(const Path *path,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
float time_offset,
|
|
|
|
|
Arrival clk_insertion,
|
|
|
|
|
Arrival clk_latency,
|
|
|
|
|
bool is_path_delay,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
ClockEdge *clk_edge = path->clkEdge(this);
|
|
|
|
|
if (clk_edge) {
|
|
|
|
|
Clock *clk = clk_edge->clock();
|
|
|
|
|
TransRiseFall *clk_tr = clk_edge->transition();
|
|
|
|
|
float clk_time = clk_edge->time() + time_offset;
|
|
|
|
|
if (clk == sdc_->defaultArrivalClock()) {
|
|
|
|
|
if (!is_path_delay) {
|
|
|
|
|
float clk_end_time = clk_time + time_offset;
|
|
|
|
|
reportLine("clock (input port clock) (rise edge)",
|
|
|
|
|
clk_end_time, clk_end_time, result);
|
|
|
|
|
reportLine(clkNetworkDelayIdealProp(false), 0.0, clk_end_time, result);
|
|
|
|
|
}
|
|
|
|
|
reportPath1(path, expanded, false, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
bool path_from_input = false;
|
|
|
|
|
bool input_has_ref_path = false;
|
|
|
|
|
Arrival clk_delay, clk_end_time;
|
|
|
|
|
const MinMax *min_max = path->minMax(this);
|
|
|
|
|
PathRef clk_path;
|
|
|
|
|
expanded.clkPath(clk_path);
|
|
|
|
|
const TransRiseFall *clk_end_tr;
|
|
|
|
|
const char *clk_name;
|
|
|
|
|
if (!clk_path.isNull()) {
|
|
|
|
|
clk_end_time = search_->clkPathArrival(&clk_path) + time_offset;
|
|
|
|
|
clk_delay = clk_end_time - clk_time;
|
|
|
|
|
clk_end_tr = clk_path.transition(this);
|
|
|
|
|
clk_name = (clk_end_tr == clk_tr)
|
|
|
|
|
? clk->name()
|
|
|
|
|
: clkNameInverted(clk->name());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Path from input port or clk used as data.
|
|
|
|
|
clk_name = clk->name();
|
|
|
|
|
clk_end_tr = clk_tr;
|
|
|
|
|
clk_delay = clk_insertion + clk_latency;
|
|
|
|
|
clk_end_time = clk_time + clk_delay;
|
|
|
|
|
|
|
|
|
|
PathRef *first_path = expanded.startPath();
|
|
|
|
|
InputDelay *input_delay = pathInputDelay(first_path);
|
|
|
|
|
if (input_delay) {
|
|
|
|
|
path_from_input = true;
|
|
|
|
|
Pin *ref_pin = input_delay->refPin();
|
|
|
|
|
if (ref_pin && clk->isPropagated()) {
|
|
|
|
|
PathRef ref_path;
|
|
|
|
|
pathInputDelayRefPath(first_path, input_delay, ref_path);
|
|
|
|
|
if (!ref_path.isNull()) {
|
|
|
|
|
const Arrival &ref_end_time = ref_path.arrival(this);
|
|
|
|
|
clk_delay = ref_end_time - clk_time;
|
|
|
|
|
clk_end_time = ref_end_time + time_offset;
|
|
|
|
|
input_has_ref_path = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool clk_used_as_data = pathFromClkPin(expanded);
|
|
|
|
|
bool is_prop = isPropagated(path);
|
|
|
|
|
const EarlyLate *early_late = min_max;
|
|
|
|
|
if (reportGenClkSrcPath(clk_path.isNull() ? NULL : &clk_path,
|
|
|
|
|
clk, clk_tr, min_max, early_late)
|
|
|
|
|
&& !(path_from_input && !input_has_ref_path)) {
|
|
|
|
|
reportClkLine(clk, clk_name, clk_end_tr, clk_time, min_max, result);
|
|
|
|
|
const PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
|
|
|
|
|
reportGenClkSrcAndPath(path, clk, clk_tr, early_late, path_ap,
|
|
|
|
|
time_offset, time_offset, clk_used_as_data,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
else if (clk_used_as_data
|
|
|
|
|
&& pathFromGenPropClk(path, path->minMax(this))) {
|
|
|
|
|
reportClkLine(clk, clk_name, clk_end_tr, clk_time, min_max, result);
|
|
|
|
|
ClkInfo *clk_info = path->tag(search_)->clkInfo();
|
|
|
|
|
if (clk_info->isPropagated())
|
2018-11-26 18:15:52 +01:00
|
|
|
reportClkSrcLatency(clk_insertion, clk_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportPath1(path, expanded, true, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
else if (is_prop
|
|
|
|
|
&& reportClkPath()
|
|
|
|
|
&& !(path_from_input && !input_has_ref_path)) {
|
2018-11-26 18:15:52 +01:00
|
|
|
reportClkLine(clk, clk_name, clk_end_tr, clk_time, early_late, result);
|
|
|
|
|
reportClkSrcLatency(clk_insertion, clk_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportPath1(path, expanded, false, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
else if (clk_used_as_data) {
|
2018-11-26 18:15:52 +01:00
|
|
|
reportClkLine(clk, clk_name, clk_end_tr, clk_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (clk_insertion > 0.0)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportClkSrcLatency(clk_insertion, clk_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportPath1(path, expanded, true, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (is_path_delay) {
|
|
|
|
|
if (clk_delay > 0.0)
|
|
|
|
|
reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay,
|
2018-11-26 18:15:52 +01:00
|
|
|
clk_end_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
reportClkLine(clk, clk_name, clk_end_tr, clk_time, min_max, result);
|
2018-11-26 18:15:52 +01:00
|
|
|
Arrival clk_arrival = clk_end_time;
|
2018-09-28 17:54:21 +02:00
|
|
|
reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay,
|
2018-11-26 18:15:52 +01:00
|
|
|
clk_arrival, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
reportPath1(path, expanded, false, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportPath1(path, expanded, false, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportTgtClk(const PathEnd *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportTgtClk(end, 0.0, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportTgtClk(const PathEnd *end,
|
|
|
|
|
float prev_time,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Clock *clk = end->targetClk(this);
|
|
|
|
|
const Path *clk_path = end->targetClkPath();
|
|
|
|
|
reportTgtClk(end, prev_time, isPropagated(clk_path, clk), result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportTgtClk(const PathEnd *end,
|
|
|
|
|
float prev_time,
|
|
|
|
|
bool is_prop,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
float src_offset = end->sourceClkOffset(this);
|
|
|
|
|
const ClockEdge *clk_edge = end->targetClkEdge(this);
|
|
|
|
|
Clock *clk = clk_edge->clock();
|
|
|
|
|
const TransRiseFall *clk_tr = clk_edge->transition();
|
|
|
|
|
const TransRiseFall *clk_end_tr = end->targetClkEndTrans(this);
|
|
|
|
|
const char *clk_name = (clk_end_tr == clk_tr)
|
|
|
|
|
? clk->name()
|
|
|
|
|
: clkNameInverted(clk->name());
|
|
|
|
|
float clk_time = prev_time
|
|
|
|
|
+ end->targetClkTime(this)
|
|
|
|
|
+ end->targetClkMcpAdjustment(this)
|
|
|
|
|
+ src_offset;
|
|
|
|
|
Arrival clk_delay = end->targetClkDelay(this);
|
|
|
|
|
Arrival clk_arrival = clk_time + clk_delay;
|
|
|
|
|
PathAnalysisPt *path_ap = end->pathAnalysisPt(this)->tgtClkAnalysisPt();
|
|
|
|
|
const MinMax *min_max = path_ap->pathMinMax();
|
|
|
|
|
const Path *clk_path = end->targetClkPath();
|
|
|
|
|
reportClkLine(clk, clk_name, clk_end_tr, prev_time, clk_time, min_max, result);
|
|
|
|
|
TimingRole *check_role = end->checkRole(this);
|
|
|
|
|
if (is_prop && reportClkPath()) {
|
|
|
|
|
float time_offset = prev_time
|
|
|
|
|
+ end->targetClkOffset(this)
|
|
|
|
|
+ end->targetClkMcpAdjustment(this);
|
|
|
|
|
const EarlyLate *early_late = check_role->tgtClkEarlyLate();
|
|
|
|
|
if (reportGenClkSrcPath(clk_path, clk, clk_tr, min_max, early_late)) {
|
|
|
|
|
float insertion_offset =
|
|
|
|
|
clk_path ? tgtClkInsertionOffet(clk_path, early_late, path_ap) : 0.0;
|
|
|
|
|
reportGenClkSrcAndPath(clk_path, clk, clk_tr, early_late, path_ap,
|
|
|
|
|
time_offset, time_offset + insertion_offset,
|
|
|
|
|
false, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Arrival insertion = end->targetClkInsertionDelay(this);
|
|
|
|
|
if (clk_path) {
|
2018-11-26 18:15:52 +01:00
|
|
|
reportClkSrcLatency(insertion, clk_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
PathExpanded clk_expanded(clk_path, this);
|
|
|
|
|
float insertion_offset = tgtClkInsertionOffet(clk_path, early_late,
|
|
|
|
|
path_ap);
|
|
|
|
|
reportPath5(clk_path, clk_expanded, 0, clk_expanded.size() - 1, is_prop,
|
|
|
|
|
reportClkPath(), delay_zero, time_offset + insertion_offset,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Output departure.
|
|
|
|
|
Arrival clk_arrival = clk_time + clk_delay;
|
|
|
|
|
reportLine(clkNetworkDelayIdealProp(clk->isPropagated()),
|
2018-11-26 18:15:52 +01:00
|
|
|
clk_delay, clk_arrival, min_max, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reportClkUncertainty(end, clk_arrival, result);
|
|
|
|
|
reportCommonClkPessimism(end, clk_arrival, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
reportLine(clkNetworkDelayIdealProp(is_prop), clk_delay, clk_arrival,
|
2018-11-26 18:15:52 +01:00
|
|
|
min_max, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportClkUncertainty(end, clk_arrival, result);
|
|
|
|
|
reportCommonClkPessimism(end, clk_arrival, result);
|
|
|
|
|
if (clk_path) {
|
|
|
|
|
Vertex *clk_vertex = clk_path->vertex(this);
|
|
|
|
|
reportLine(descriptionField(clk_vertex),
|
|
|
|
|
prev_time
|
|
|
|
|
+ end->targetClkArrival(this)
|
|
|
|
|
+ end->sourceClkOffset(this),
|
2018-11-26 18:15:52 +01:00
|
|
|
min_max, clk_end_tr, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
ReportPath::tgtClkInsertionOffet(const Path *clk_path,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
PathAnalysisPt *path_ap)
|
|
|
|
|
{
|
|
|
|
|
ClkInfo *clk_info = clk_path->clkInfo(this);
|
|
|
|
|
const Pin *src_pin = clk_info->clkSrc();
|
|
|
|
|
const ClockEdge *clk_edge = clk_info->clkEdge();
|
|
|
|
|
const Clock *clk = clk_edge->clock();
|
|
|
|
|
const TransRiseFall *clk_tr = clk_edge->transition();
|
|
|
|
|
const MinMax *min_max = path_ap->pathMinMax();
|
|
|
|
|
Arrival path_insertion = search_->clockInsertion(clk, src_pin, clk_tr,
|
|
|
|
|
min_max, min_max,
|
|
|
|
|
path_ap);
|
|
|
|
|
Arrival tgt_insertion = search_->clockInsertion(clk, src_pin, clk_tr,
|
|
|
|
|
min_max, early_late,
|
|
|
|
|
path_ap);
|
|
|
|
|
return delayAsFloat(tgt_insertion - path_insertion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::pathFromGenPropClk(const Path *clk_path,
|
|
|
|
|
const EarlyLate *early_late)
|
|
|
|
|
{
|
|
|
|
|
ClkInfo *clk_info = clk_path->tag(search_)->clkInfo();
|
|
|
|
|
const ClockEdge *clk_edge = clk_info->clkEdge();
|
|
|
|
|
if (clk_edge) {
|
|
|
|
|
const Clock *clk = clk_edge->clock();
|
|
|
|
|
float insertion;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->clockInsertion(clk, clk_info->clkSrc(),
|
|
|
|
|
clk_edge->transition(),
|
|
|
|
|
clk_path->minMax(this),
|
|
|
|
|
early_late,
|
|
|
|
|
insertion, exists);
|
|
|
|
|
return !exists
|
|
|
|
|
&& clk->isGeneratedWithPropagatedMaster();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::isGenPropClk(const Clock *clk,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const EarlyLate *early_late)
|
|
|
|
|
{
|
|
|
|
|
float insertion;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->clockInsertion(clk, clk->srcPin(), clk_tr,
|
|
|
|
|
min_max, early_late,
|
|
|
|
|
insertion, exists);
|
|
|
|
|
return !exists
|
|
|
|
|
&& clk->isGeneratedWithPropagatedMaster();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportClkLine(const Clock *clk,
|
|
|
|
|
const char *clk_name,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
Arrival clk_time,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportClkLine(clk, clk_name, clk_tr, 0.0, clk_time, min_max, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportClkLine(const Clock *clk,
|
|
|
|
|
const char *clk_name,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
Arrival prev_time,
|
|
|
|
|
Arrival clk_time,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const char *rise_fall = asRiseFall(clk_tr);
|
|
|
|
|
const char *clk_msg = stringPrintTmp(strlen("clock ( edge)")
|
|
|
|
|
+ strlen(clk_name)
|
|
|
|
|
+ strlen(rise_fall) + 1,
|
|
|
|
|
"clock %s (%s edge)",
|
|
|
|
|
clk_name,
|
|
|
|
|
rise_fall);
|
|
|
|
|
if (clk->isPropagated())
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(clk_msg, clk_time - prev_time, clk_time, min_max, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
|
|
|
|
// Report ideal clock slew.
|
|
|
|
|
float clk_slew = clk->slew(clk_tr, min_max);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(clk_msg, clk_slew, clk_time - prev_time, clk_time,
|
|
|
|
|
min_max, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::reportGenClkSrcPath(const Path *clk_path,
|
|
|
|
|
Clock *clk,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const EarlyLate *early_late)
|
|
|
|
|
{
|
|
|
|
|
bool from_gen_prop_clk = clk_path
|
|
|
|
|
? pathFromGenPropClk(clk_path, early_late)
|
|
|
|
|
: isGenPropClk(clk, clk_tr, min_max, early_late);
|
|
|
|
|
return from_gen_prop_clk
|
|
|
|
|
&& format_ == report_path_full_clock_expanded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportGenClkSrcAndPath(const Path *path,
|
|
|
|
|
Clock *clk,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
const PathAnalysisPt *path_ap,
|
|
|
|
|
float time_offset,
|
|
|
|
|
float path_time_offset,
|
|
|
|
|
bool clk_used_as_data,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const Pin *clk_pin = path
|
|
|
|
|
? path->clkInfo(search_)->clkSrc()
|
|
|
|
|
: clk->defaultPin();
|
|
|
|
|
float gclk_time = clk->edge(clk_tr)->time() + time_offset;
|
|
|
|
|
bool skip_first_path = reportGenClkSrcPath1(clk, clk_pin, clk_tr,
|
|
|
|
|
early_late, path_ap, gclk_time,
|
|
|
|
|
time_offset, clk_used_as_data,
|
|
|
|
|
result);
|
|
|
|
|
if (path) {
|
|
|
|
|
PathExpanded expanded(path, this);
|
|
|
|
|
reportPath4(path, expanded, skip_first_path, false, clk_used_as_data,
|
|
|
|
|
path_time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::reportGenClkSrcPath1(Clock *clk,
|
|
|
|
|
const Pin *clk_pin,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
const PathAnalysisPt *path_ap,
|
|
|
|
|
float gclk_time,
|
|
|
|
|
float time_offset,
|
|
|
|
|
bool clk_used_as_data,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathAnalysisPt *insert_ap = path_ap->insertionAnalysisPt(early_late);
|
|
|
|
|
PathVertex src_path;
|
2018-11-26 18:15:52 +01:00
|
|
|
const MinMax *min_max = path_ap->pathMinMax();
|
2018-09-28 17:54:21 +02:00
|
|
|
search_->genclks()->srcPath(clk, clk_pin, clk_tr, insert_ap, src_path);
|
|
|
|
|
if (!src_path.isNull()) {
|
|
|
|
|
ClkInfo *src_clk_info = src_path.clkInfo(search_);
|
|
|
|
|
ClockEdge *src_clk_edge = src_clk_info->clkEdge();
|
|
|
|
|
Clock *src_clk = src_clk_info->clock();
|
|
|
|
|
bool skip_first_path = false;
|
|
|
|
|
const TransRiseFall *src_clk_tr = src_clk_edge->transition();
|
|
|
|
|
const Pin *src_clk_pin = src_clk_info->clkSrc();
|
|
|
|
|
if (src_clk->isGeneratedWithPropagatedMaster()
|
|
|
|
|
&& src_clk_info->isPropagated()) {
|
|
|
|
|
skip_first_path = reportGenClkSrcPath1(src_clk, src_clk_pin,
|
|
|
|
|
src_clk_tr, early_late, path_ap,
|
|
|
|
|
gclk_time, time_offset,
|
|
|
|
|
clk_used_as_data, result);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const Arrival insertion = search_->clockInsertion(src_clk, src_clk_pin,
|
|
|
|
|
src_clk_tr,
|
|
|
|
|
path_ap->pathMinMax(),
|
|
|
|
|
early_late, path_ap);
|
2018-11-26 18:15:52 +01:00
|
|
|
reportClkSrcLatency(insertion, gclk_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
PathExpanded src_expanded(&src_path, this);
|
|
|
|
|
if (clk->pllOut()) {
|
|
|
|
|
reportPath4(&src_path, src_expanded, skip_first_path, true,
|
|
|
|
|
clk_used_as_data, gclk_time, result);
|
|
|
|
|
PathAnalysisPt *pll_ap=path_ap->insertionAnalysisPt(min_max->opposite());
|
|
|
|
|
Arrival pll_delay = search_->genclks()->pllDelay(clk, clk_tr, pll_ap);
|
|
|
|
|
size_t path_length = src_expanded.size();
|
|
|
|
|
if (path_length < 2)
|
|
|
|
|
internalError("generated clock pll source path too short.\n");
|
|
|
|
|
PathRef *path0 = src_expanded.path(path_length - 2);
|
|
|
|
|
Arrival time0 = path0->arrival(this) + gclk_time;
|
|
|
|
|
PathRef *path1 = src_expanded.path(path_length - 1);
|
|
|
|
|
reportPathLine(path1, -pll_delay, time0 - pll_delay, "pll_delay", result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportPath4(&src_path, src_expanded, skip_first_path, false,
|
|
|
|
|
clk_used_as_data, gclk_time, result);
|
|
|
|
|
if (!clk->isPropagated())
|
|
|
|
|
reportLine("clock network delay (ideal)", 0.0, src_path.arrival(this),
|
2018-11-26 18:15:52 +01:00
|
|
|
min_max, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (clk->isPropagated())
|
2018-11-26 18:15:52 +01:00
|
|
|
reportClkSrcLatency(0.0, gclk_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else if (!clk_used_as_data)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("clock network delay (ideal)", 0.0, gclk_time,
|
|
|
|
|
min_max, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
return !src_path.isNull();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportClkSrcLatency(Arrival insertion,
|
|
|
|
|
float clk_time,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("clock source latency", insertion, clk_time + insertion,
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPathLine(const Path *path,
|
|
|
|
|
Arrival incr,
|
|
|
|
|
Arrival time,
|
|
|
|
|
const char *line_case,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Vertex *vertex = path->vertex(this);
|
|
|
|
|
Pin *pin = vertex->pin();
|
|
|
|
|
const char *what = descriptionField(vertex);
|
|
|
|
|
const TransRiseFall *tr = path->transition(this);
|
|
|
|
|
bool is_driver = network_->isDriver(pin);
|
2018-11-26 18:15:52 +01:00
|
|
|
PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
|
|
|
|
|
const EarlyLate *early_late = path_ap->pathMinMax();
|
|
|
|
|
DcalcAnalysisPt *dcalc_ap = path_ap->dcalcAnalysisPt();
|
2018-09-28 17:54:21 +02:00
|
|
|
DcalcAPIndex ap_index = dcalc_ap->index();
|
|
|
|
|
Slew slew = graph_->slew(vertex, tr, ap_index);
|
|
|
|
|
float cap = field_blank_;
|
|
|
|
|
// Don't show capacitance field for input pins.
|
|
|
|
|
if (is_driver && field_capacitance_->enabled())
|
|
|
|
|
cap = loadCap(pin, tr, dcalc_ap);
|
|
|
|
|
reportLine(what, cap, slew, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
incr, time, false, early_late, tr, line_case, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportRequired(const PathEnd *end,
|
|
|
|
|
const char *margin_msg,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
Required req_time = end->requiredTimeOffset(this);
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = end->clkEarlyLate(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
ArcDelay margin = end->margin(this);
|
|
|
|
|
if (end->minMax(this) == MinMax::max())
|
|
|
|
|
margin = -margin;
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine(margin_msg, margin, req_time, early_late, result);
|
|
|
|
|
reportLine("data required time", req_time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlack(const PathEnd *end,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = end->pathEarlyLate(this);
|
|
|
|
|
reportLine("data required time", end->requiredTimeOffset(this),
|
|
|
|
|
early_late->opposite(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportLineNegative("data arrival time", end->dataArrivalTimeOffset(this),
|
2018-11-26 18:15:52 +01:00
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
reportDashLine(result);
|
|
|
|
|
reportSlack(end->slack(this), result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSlack(Slack slack,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
const char *msg = (slack < 0.0)
|
|
|
|
|
? "slack (VIOLATED)"
|
|
|
|
|
: "slack (MET)";
|
|
|
|
|
reportLine(msg, slack, EarlyLate::early(), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportCommonClkPessimism(const PathEnd *end,
|
|
|
|
|
Arrival &clk_arrival,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
if (sdc_->crprEnabled()) {
|
|
|
|
|
float pessimism = end->commonClkPessimism(this);
|
|
|
|
|
clk_arrival += pessimism;
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("clock reconvergence pessimism", pessimism, clk_arrival,
|
|
|
|
|
end->clkEarlyLate(this), result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportClkUncertainty(const PathEnd *end,
|
|
|
|
|
Arrival &clk_arrival,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = end->clkEarlyLate(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
float uncertainty = end->targetNonInterClkUncertainty(this);
|
|
|
|
|
clk_arrival += uncertainty;
|
|
|
|
|
if (uncertainty != 0.0)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("clock uncertainty", uncertainty, clk_arrival,
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
float inter_uncertainty = end->interClkUncertainty(this);
|
|
|
|
|
clk_arrival += inter_uncertainty;
|
|
|
|
|
if (inter_uncertainty != 0.0)
|
|
|
|
|
reportLine("inter-clock uncertainty", inter_uncertainty, clk_arrival,
|
2018-11-26 18:15:52 +01:00
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPath(const PathEnd *end,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportPathHeader(result);
|
|
|
|
|
// Source clk offset for path delays removes clock phase time.
|
|
|
|
|
float src_clk_offset = end->sourceClkOffset(this);
|
|
|
|
|
reportPath1(end->path(), expanded, pathFromClkPin(expanded),
|
|
|
|
|
src_clk_offset, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPath(const Path *path,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportPathHeader(result);
|
|
|
|
|
PathExpanded expanded(path, this);
|
|
|
|
|
reportSrcClkAndPath(path, expanded, 0.0, delay_zero, delay_zero,
|
|
|
|
|
false, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPath1(const Path *path,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
bool clk_used_as_data,
|
|
|
|
|
float time_offset,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
PathRef *d_path, *q_path;
|
|
|
|
|
Edge *d_q_edge;
|
|
|
|
|
expanded.latchPaths(d_path, q_path, d_q_edge);
|
|
|
|
|
if (d_path) {
|
|
|
|
|
Arrival latch_time_given, latch_enable_time;
|
|
|
|
|
PathVertex latch_enable_path;
|
|
|
|
|
latches_->latchTimeGivenToStartpoint(d_path, q_path, d_q_edge,
|
|
|
|
|
latch_time_given,
|
|
|
|
|
latch_enable_path);
|
|
|
|
|
if (!latch_enable_path.isNull()) {
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = latch_enable_path.minMax(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
latch_enable_time = search_->clkPathArrival(&latch_enable_path);
|
|
|
|
|
if (reportClkPath()) {
|
|
|
|
|
PathExpanded enable_expanded(&latch_enable_path, this);
|
|
|
|
|
// Report the path to the latch enable.
|
|
|
|
|
reportPath2(&latch_enable_path, enable_expanded, false,
|
|
|
|
|
time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
Arrival time = latch_enable_time + latch_time_given;
|
|
|
|
|
Arrival incr = latch_time_given;
|
|
|
|
|
if (incr >= 0.0)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("time given to startpoint", incr, time, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("time borrowed from startpoint", incr, time,
|
|
|
|
|
early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
// Override latch D arrival with enable + given.
|
|
|
|
|
reportPathLine(expanded.path(0), delay_zero, time, "latch_D", result);
|
|
|
|
|
bool propagated_clk = path->clkInfo(search_)->isPropagated();
|
|
|
|
|
bool report_clk_path = path->isClock(search_) || reportClkPath();
|
|
|
|
|
reportPath5(path, expanded, 1, expanded.size() - 1,
|
|
|
|
|
propagated_clk, report_clk_path,
|
|
|
|
|
latch_enable_time + latch_time_given, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportPath2(path, expanded, clk_used_as_data, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPath2(const Path *path,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
bool clk_used_as_data,
|
|
|
|
|
float time_offset,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
// Report the clock path if the end is a clock or we wouldn't have
|
|
|
|
|
// anything to report.
|
|
|
|
|
bool report_clk_path = clk_used_as_data
|
|
|
|
|
|| (reportClkPath()
|
|
|
|
|
&& path->clkInfo(search_)->isPropagated());
|
|
|
|
|
reportPath3(path, expanded, clk_used_as_data, report_clk_path,
|
|
|
|
|
delay_zero, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPath3(const Path *path,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
bool clk_used_as_data,
|
|
|
|
|
bool report_clk_path,
|
|
|
|
|
Arrival prev_time,
|
|
|
|
|
float time_offset,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
bool propagated_clk = clk_used_as_data
|
|
|
|
|
|| path->clkInfo(search_)->isPropagated();
|
|
|
|
|
size_t path_last_index = expanded.size() - 1;
|
|
|
|
|
reportPath5(path, expanded, 0, path_last_index, propagated_clk,
|
|
|
|
|
report_clk_path, prev_time, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPath4(const Path *path,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
bool skip_first_path,
|
|
|
|
|
bool skip_last_path,
|
|
|
|
|
bool clk_used_as_data,
|
|
|
|
|
float time_offset,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
size_t path_first_index = 0;
|
|
|
|
|
Arrival prev_time(0.0);
|
|
|
|
|
if (skip_first_path) {
|
|
|
|
|
path_first_index = 1;
|
|
|
|
|
PathRef *start = expanded.path(0);
|
|
|
|
|
prev_time = start->arrival(this) + time_offset;
|
|
|
|
|
}
|
|
|
|
|
size_t path_last_index = expanded.size() - 1;
|
|
|
|
|
if (skip_last_path
|
|
|
|
|
&& path_last_index > 1)
|
|
|
|
|
path_last_index--;
|
|
|
|
|
bool propagated_clk = clk_used_as_data
|
|
|
|
|
|| path->clkInfo(search_)->isPropagated();
|
|
|
|
|
// Report the clock path if the end is a clock or we wouldn't have
|
|
|
|
|
// anything to report.
|
|
|
|
|
bool report_clk_path = path->isClock(search_)
|
|
|
|
|
|| (reportClkPath() && propagated_clk);
|
|
|
|
|
reportPath5(path, expanded, path_first_index, path_last_index,
|
|
|
|
|
propagated_clk, report_clk_path, prev_time, time_offset, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPath5(const Path *path,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
size_t path_first_index,
|
|
|
|
|
size_t path_last_index,
|
|
|
|
|
bool propagated_clk,
|
|
|
|
|
bool report_clk_path,
|
|
|
|
|
Arrival prev_time,
|
|
|
|
|
float time_offset,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const MinMax *min_max = path->minMax(this);
|
|
|
|
|
DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt();
|
|
|
|
|
DcalcAPIndex ap_index = dcalc_ap->index();
|
|
|
|
|
for (size_t i = path_first_index; i <= path_last_index; i++) {
|
|
|
|
|
PathRef *path1 = expanded.path(i);
|
|
|
|
|
TimingArc *prev_arc = expanded.prevArc(i);
|
|
|
|
|
Vertex *vertex = path1->vertex(this);
|
|
|
|
|
Pin *pin = vertex->pin();
|
|
|
|
|
Arrival time = path1->arrival(this) + time_offset;
|
2018-11-26 18:15:52 +01:00
|
|
|
float incr = 0.0;
|
2018-09-28 17:54:21 +02:00
|
|
|
const char *line_case = NULL;
|
|
|
|
|
bool is_clk_start = network_->isRegClkPin(pin);
|
|
|
|
|
bool is_clk = path1->isClock(search_);
|
|
|
|
|
// Always show the search start point (register clk pin).
|
|
|
|
|
// Skip reporting the clk tree unless it is requested.
|
|
|
|
|
if (is_clk_start
|
|
|
|
|
|| report_clk_path
|
|
|
|
|
|| !is_clk) {
|
|
|
|
|
const TransRiseFall *tr = path1->transition(this);
|
|
|
|
|
Slew slew = graph_->slew(vertex, tr, ap_index);
|
|
|
|
|
if (prev_arc == NULL) {
|
|
|
|
|
// First path.
|
|
|
|
|
reportInputExternalDelay(path1, time_offset, result);
|
|
|
|
|
size_t next_index = i + 1;
|
|
|
|
|
const PathRef *next_path = expanded.path(next_index);
|
|
|
|
|
if (network_->isTopLevelPort(pin)
|
|
|
|
|
&& next_path
|
|
|
|
|
&& !nextArcAnnotated(next_path, next_index, expanded, ap_index)
|
|
|
|
|
&& hasExtInputDriver(pin, tr, min_max)) {
|
|
|
|
|
// Pin is an input port with drive_cell/drive_resistance.
|
|
|
|
|
// The delay calculator annotates wire delays on the edges
|
|
|
|
|
// from the input to the loads. Report the wire delay on the
|
|
|
|
|
// input pin instead.
|
|
|
|
|
Arrival next_time = next_path->arrival(this) + time_offset;
|
2018-12-05 23:18:41 +01:00
|
|
|
incr = delayAsFloat(next_time, min_max)
|
|
|
|
|
- delayAsFloat(time, min_max);
|
2018-09-28 17:54:21 +02:00
|
|
|
time = next_time;
|
|
|
|
|
line_case = "input_drive";
|
|
|
|
|
}
|
|
|
|
|
else if (is_clk) {
|
|
|
|
|
if (!propagated_clk)
|
|
|
|
|
// Clock latency at path endpoint in case latency was set
|
|
|
|
|
// on a clock pin other than the clock source.
|
|
|
|
|
time = search_->clkPathArrival(path1) + time_offset;
|
|
|
|
|
incr = 0.0;
|
|
|
|
|
line_case = "clk_first";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
incr = 0.0;
|
|
|
|
|
line_case = "first";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (is_clk_start
|
|
|
|
|
&& is_clk
|
|
|
|
|
&& !report_clk_path) {
|
|
|
|
|
// Clock start point and clock path are not reported.
|
|
|
|
|
incr = 0.0;
|
|
|
|
|
if (!propagated_clk) {
|
|
|
|
|
// Ideal clock.
|
|
|
|
|
ClockEdge *src_clk_edge = path->clkEdge(this);
|
|
|
|
|
time = search_->clkPathArrival(path1) + time_offset;
|
|
|
|
|
if (src_clk_edge) {
|
|
|
|
|
Clock *src_clk = src_clk_edge->clock();
|
|
|
|
|
TransRiseFall *src_clk_tr = src_clk_edge->transition();
|
|
|
|
|
slew = src_clk->slew(src_clk_tr, min_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
line_case = "clk_start";
|
|
|
|
|
}
|
|
|
|
|
else if (is_clk
|
|
|
|
|
&& report_clk_path
|
|
|
|
|
&& !propagated_clk) {
|
|
|
|
|
// Zero the clock network delays for ideal clocks.
|
|
|
|
|
incr = 0.0;
|
|
|
|
|
time = prev_time;
|
|
|
|
|
ClockEdge *src_clk_edge = path->clkEdge(this);
|
|
|
|
|
Clock *src_clk = src_clk_edge->clock();
|
|
|
|
|
TransRiseFall *src_clk_tr = src_clk_edge->transition();
|
|
|
|
|
slew = src_clk->slew(src_clk_tr, min_max);
|
|
|
|
|
line_case = "clk_ideal";
|
|
|
|
|
}
|
|
|
|
|
else if (is_clk && !is_clk_start) {
|
2018-12-05 23:18:41 +01:00
|
|
|
incr = delayAsFloat(time, min_max)
|
|
|
|
|
- delayAsFloat(prev_time, min_max);
|
2018-09-28 17:54:21 +02:00
|
|
|
line_case = "clk_prop";
|
|
|
|
|
}
|
|
|
|
|
else {
|
2018-12-05 23:18:41 +01:00
|
|
|
incr = delayAsFloat(time, min_max)
|
|
|
|
|
- delayAsFloat(prev_time, min_max);
|
2018-09-28 17:54:21 +02:00
|
|
|
line_case = "normal";
|
|
|
|
|
}
|
|
|
|
|
if (report_input_pin_
|
|
|
|
|
|| (i == path_last_index)
|
|
|
|
|
|| is_clk_start
|
|
|
|
|
|| (prev_arc == NULL)
|
|
|
|
|
// Filter wire edges from report unless reporting
|
|
|
|
|
// input pins.
|
|
|
|
|
|| (prev_arc
|
|
|
|
|
&& !prev_arc->role()->isWire())) {
|
|
|
|
|
bool is_driver = network_->isDriver(pin);
|
|
|
|
|
float cap = field_blank_;
|
|
|
|
|
// Don't show capacitance field for input pins.
|
|
|
|
|
if (is_driver && field_capacitance_->enabled())
|
|
|
|
|
cap = loadCap(pin, tr, dcalc_ap);
|
|
|
|
|
const char *what = descriptionField(vertex);
|
|
|
|
|
if (report_net_ && is_driver) {
|
|
|
|
|
// Capacitance field is reported on the net line.
|
|
|
|
|
reportLine(what, field_blank_, slew, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
incr, time, false, min_max, tr, line_case, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(pin);
|
|
|
|
|
what = stringPrintTmp(strlen(" (net)")
|
|
|
|
|
+ strlen(pin_name) + 1,
|
|
|
|
|
"%s (net)", pin_name);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Net *net = network_->net(pin);
|
|
|
|
|
if (net) {
|
|
|
|
|
Net *highest_net = network_->highestNetAbove(net);
|
|
|
|
|
const char *net_name = cmd_network_->pathName(highest_net);
|
|
|
|
|
what = stringPrintTmp(strlen(" (net)")
|
|
|
|
|
+ strlen(net_name) + 1,
|
|
|
|
|
"%s (net)", net_name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
what = "(unconnected)";
|
|
|
|
|
}
|
|
|
|
|
float fanout = drvrFanout(vertex, min_max);
|
|
|
|
|
reportLine(what, cap, field_blank_, fanout,
|
2018-11-26 18:15:52 +01:00
|
|
|
field_blank_, field_blank_, false, min_max, NULL,
|
2018-09-28 17:54:21 +02:00
|
|
|
line_case, result);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
reportLine(what, cap, slew, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
incr, time, false, min_max, tr, line_case, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
prev_time = time;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
prev_time = time;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::nextArcAnnotated(const PathRef *next_path,
|
|
|
|
|
size_t next_index,
|
|
|
|
|
PathExpanded &expanded,
|
|
|
|
|
DcalcAPIndex ap_index)
|
|
|
|
|
{
|
|
|
|
|
TimingArc *arc = expanded.prevArc(next_index);
|
|
|
|
|
Edge *edge = next_path->prevEdge(arc, this);
|
2018-11-09 19:04:16 +01:00
|
|
|
return graph_->arcDelayAnnotated(edge, arc, ap_index);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
ReportPath::descriptionField(Vertex *vertex)
|
|
|
|
|
{
|
|
|
|
|
Pin *pin = vertex->pin();
|
|
|
|
|
const char *pin_name = cmd_network_->pathName(pin);
|
|
|
|
|
const char *name2;
|
|
|
|
|
if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
PortDirection *dir = network_->direction(pin);
|
|
|
|
|
// Translate port direction. Note that this is intentionally
|
|
|
|
|
// inconsistent with the direction reported for top level ports as
|
|
|
|
|
// startpoints.
|
|
|
|
|
if (dir->isInput())
|
|
|
|
|
name2 = "in";
|
|
|
|
|
else if (dir->isOutput() || dir->isTristate())
|
|
|
|
|
name2 = "out";
|
|
|
|
|
else if (dir->isBidirect())
|
|
|
|
|
name2 = "inout";
|
|
|
|
|
else
|
|
|
|
|
name2 = "?";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
name2 = network_->cellName(inst);
|
|
|
|
|
}
|
|
|
|
|
// stringPrintTmp("%s (%s)", pin_name, name2)
|
|
|
|
|
size_t pin_name_length = strlen(pin_name);
|
|
|
|
|
size_t name2_length = strlen(name2);
|
|
|
|
|
char *result = makeTmpString(pin_name_length + name2_length
|
|
|
|
|
+ 4 /* strlen(" ()") + '\0' */);
|
|
|
|
|
char *s = result;
|
|
|
|
|
strcpy(s, pin_name);
|
|
|
|
|
s += pin_name_length;
|
|
|
|
|
*s++ = ' ';
|
|
|
|
|
*s++ = '(';
|
|
|
|
|
strcpy(s, name2);
|
|
|
|
|
s += name2_length;
|
|
|
|
|
*s++ = ')';
|
|
|
|
|
*s++ = '\0';
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
ReportPath::drvrFanout(Vertex *drvr,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
float fanout = 0.0;
|
|
|
|
|
VertexOutEdgeIterator iter(drvr, graph_);
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
Edge *edge = iter.next();
|
|
|
|
|
Pin *pin = edge->to(graph_)->pin();
|
|
|
|
|
if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
// Output port counts as a fanout.
|
|
|
|
|
Port *port = network_->port(pin);
|
|
|
|
|
fanout += sdc_->portExtFanout(port, min_max) + 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
fanout++;
|
|
|
|
|
}
|
|
|
|
|
return fanout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::hasExtInputDriver(const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
Port *port = network_->port(pin);
|
|
|
|
|
InputDrive *drive = sdc_->findInputDrive(port);
|
|
|
|
|
return (drive
|
|
|
|
|
&& (drive->hasDriveResistance(tr, min_max)
|
|
|
|
|
|| drive->hasDriveCell(tr, min_max)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportInputExternalDelay(const Path *first_path,
|
|
|
|
|
float time_offset,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
const Pin *first_pin = first_path->pin(graph_);
|
|
|
|
|
if (!pathFromClkPin(first_path, first_pin)) {
|
|
|
|
|
const TransRiseFall *tr = first_path->transition(this);
|
|
|
|
|
Arrival time = first_path->arrival(this) + time_offset;
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late = first_path->minMax(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
InputDelay *input_delay = pathInputDelay(first_path);
|
|
|
|
|
if (input_delay) {
|
|
|
|
|
const Pin *ref_pin = input_delay->refPin();
|
|
|
|
|
if (ref_pin) {
|
|
|
|
|
PathRef ref_path;
|
|
|
|
|
pathInputDelayRefPath(first_path, input_delay, ref_path);
|
|
|
|
|
if (!ref_path.isNull() && reportClkPath()) {
|
|
|
|
|
PathExpanded ref_expanded(&ref_path, this);
|
|
|
|
|
float ref_offset = time_offset;
|
|
|
|
|
ClkInfo *ref_clk_info = ref_path.clkInfo(this);
|
|
|
|
|
// Ref clk does not include latency for non-ideal clocks.
|
|
|
|
|
// Remove it to compensate for reportPath5 adding it.
|
|
|
|
|
if (!ref_clk_info->isPropagated())
|
|
|
|
|
ref_offset -= ref_clk_info->latency();
|
|
|
|
|
reportPath3(&ref_path, ref_expanded, false, true,
|
|
|
|
|
delay_zero, 0.0, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
float input_arrival =
|
|
|
|
|
input_delay->delays()->value(tr, first_path->minMax(this));
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("input external delay", input_arrival, time,
|
|
|
|
|
early_late, tr, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else if (network_->isTopLevelPort(first_pin))
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLine("input external delay", 0.0, time, early_late, tr, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the input delay at the start of a path.
|
|
|
|
|
InputDelay *
|
|
|
|
|
ReportPath::pathInputDelay(const Path *first_path) const
|
|
|
|
|
{
|
|
|
|
|
return first_path->tag(this)->inputDelay();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::pathInputDelayRefPath(const Path *path,
|
|
|
|
|
InputDelay *input_delay,
|
|
|
|
|
// Return value.
|
|
|
|
|
PathRef &ref_path)
|
|
|
|
|
{
|
|
|
|
|
Pin *ref_pin = input_delay->refPin();
|
|
|
|
|
TransRiseFall *ref_tr = input_delay->refTransition();
|
|
|
|
|
Vertex *ref_vertex = graph_->pinDrvrVertex(ref_pin);
|
|
|
|
|
const PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
|
|
|
|
|
const ClockEdge *clk_edge = path->clkEdge(this);
|
|
|
|
|
VertexPathIterator path_iter(ref_vertex, ref_tr, path_ap, this);
|
|
|
|
|
while (path_iter.hasNext()) {
|
|
|
|
|
PathVertex *path = path_iter.next();
|
|
|
|
|
if (path->isClock(this)
|
|
|
|
|
&& path->clkEdge(this) == clk_edge) {
|
|
|
|
|
ref_path.init(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
ReportPath::loadCap(Pin *drvr_pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
DcalcAnalysisPt *dcalc_ap)
|
|
|
|
|
{
|
|
|
|
|
Parasitic *parasitic = NULL;
|
|
|
|
|
bool delete_parasitic;
|
|
|
|
|
if (arc_delay_calc_)
|
|
|
|
|
arc_delay_calc_->findParasitic(drvr_pin, tr, dcalc_ap,
|
|
|
|
|
parasitic, delete_parasitic);
|
|
|
|
|
float cap = graph_delay_calc_->loadCap(drvr_pin, parasitic, tr, dcalc_ap);
|
|
|
|
|
if (arc_delay_calc_)
|
|
|
|
|
arc_delay_calc_->finish(drvr_pin, tr, dcalc_ap,
|
|
|
|
|
parasitic, delete_parasitic);
|
|
|
|
|
return cap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportPathHeader(string &result)
|
|
|
|
|
{
|
|
|
|
|
ReportFieldSeq::Iterator field_iter(fields_);
|
|
|
|
|
bool first_field = true;
|
|
|
|
|
while (field_iter.hasNext()) {
|
|
|
|
|
ReportField *field = field_iter.next();
|
|
|
|
|
if (field->enabled()) {
|
|
|
|
|
if (!first_field)
|
|
|
|
|
result += ' ';
|
|
|
|
|
first_field = false;
|
|
|
|
|
|
|
|
|
|
reportField(field->title(), field, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
trimRight(result);
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
|
|
|
|
|
reportDashLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Report total.
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLine(const char *what,
|
|
|
|
|
Delay total,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportLine(what, field_blank_, field_blank_, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
field_blank_, total, false, early_late, NULL, NULL, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Report negative total.
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLineNegative(const char *what,
|
|
|
|
|
Delay total,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportLine(what, field_blank_, field_blank_, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
field_blank_, total, true, early_late, NULL, NULL, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Report total, and transition suffix.
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLine(const char *what,
|
|
|
|
|
Delay total,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportLine(what, field_blank_, field_blank_, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
field_blank_, total, false, early_late, tr, NULL, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Report increment, and total.
|
2018-11-26 18:15:52 +01:00
|
|
|
void
|
|
|
|
|
ReportPath::reportLine(const char *what,
|
|
|
|
|
float incr,
|
|
|
|
|
float total,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportLine(what, field_blank_, field_blank_, field_blank_,
|
|
|
|
|
incr, total, false, NULL, NULL, NULL, result);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
void
|
|
|
|
|
ReportPath::reportLine(const char *what,
|
|
|
|
|
Delay incr,
|
|
|
|
|
Delay total,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportLine(what, field_blank_, field_blank_, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
incr, total, false, early_late, NULL, NULL, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Report increment, total, and transition suffix.
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLine(const char *what,
|
|
|
|
|
Delay incr,
|
|
|
|
|
Delay total,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportLine(what, field_blank_, field_blank_, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
incr, total, false, early_late, tr, NULL, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Report slew, increment, and total.
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLine(const char *what,
|
|
|
|
|
Slew slew,
|
|
|
|
|
Delay incr,
|
|
|
|
|
Delay total,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportLine(what, field_blank_, slew, field_blank_,
|
2018-11-26 18:15:52 +01:00
|
|
|
incr, total, false, early_late, NULL, NULL, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLine(const char *what,
|
|
|
|
|
float cap,
|
|
|
|
|
Slew slew,
|
|
|
|
|
float fanout,
|
|
|
|
|
Delay incr,
|
|
|
|
|
Delay total,
|
|
|
|
|
bool total_with_minus,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const char *line_case,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
ReportFieldSeq::Iterator field_iter(fields_);
|
|
|
|
|
bool first_field = true;
|
|
|
|
|
while (field_iter.hasNext()) {
|
|
|
|
|
ReportField *field = field_iter.next();
|
|
|
|
|
if (field->enabled()) {
|
|
|
|
|
if (!first_field
|
|
|
|
|
// Compatibility kludge; suppress trailing whitespace for edge.
|
|
|
|
|
&& field != field_edge_)
|
|
|
|
|
result += ' ';
|
|
|
|
|
first_field = false;
|
|
|
|
|
|
|
|
|
|
if (field == field_description_)
|
|
|
|
|
reportDescription(what, result);
|
|
|
|
|
else if (field == field_fanout_) {
|
|
|
|
|
if (fanout == field_blank_)
|
|
|
|
|
reportFieldBlank(field, result);
|
|
|
|
|
else {
|
|
|
|
|
char *field = stringPrintTmp(field_fanout_->width() + 1, "%*d",
|
|
|
|
|
field_fanout_->width(),
|
|
|
|
|
static_cast<int>(fanout));
|
|
|
|
|
result += field;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (field == field_capacitance_)
|
|
|
|
|
reportField(cap, field, result);
|
|
|
|
|
else if (field == field_slew_)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportFieldDelay(slew, early_late, field, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else if (field == field_incr_)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportFieldDelay(incr, early_late, field, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else if (field == field_total_) {
|
2018-11-26 18:15:52 +01:00
|
|
|
if (total_with_minus)
|
|
|
|
|
reportFieldDelayMinus(total, early_late, field, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2018-11-26 18:15:52 +01:00
|
|
|
reportFieldDelay(total, early_late, field, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else if (field == field_edge_) {
|
|
|
|
|
if (tr) {
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportField(tr->shortName(), field, result);
|
|
|
|
|
}
|
|
|
|
|
// Compatibility kludge; suppress trailing spaces.
|
|
|
|
|
else if (field_iter.hasNext()) {
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportFieldBlank(field, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (field == field_case_ && line_case)
|
|
|
|
|
result += line_case;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Only the total field.
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLineTotal(const char *what,
|
|
|
|
|
Delay incr,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal1(what, incr, false, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only the total field and always with leading minus sign.
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLineTotalMinus(const char *what,
|
|
|
|
|
Delay decr,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
reportLineTotal1(what, decr, true, early_late, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportLineTotal1(const char *what,
|
|
|
|
|
Delay incr,
|
|
|
|
|
bool incr_with_minus,
|
2018-11-26 18:15:52 +01:00
|
|
|
const EarlyLate *early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
reportDescription(what, result);
|
|
|
|
|
result += ' ';
|
|
|
|
|
if (incr_with_minus)
|
2018-11-26 18:15:52 +01:00
|
|
|
reportFieldDelayMinus(incr, early_late, field_total_, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2018-11-26 18:15:52 +01:00
|
|
|
reportFieldDelay(incr, early_late, field_total_, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportDashLineTotal(string &result)
|
|
|
|
|
{
|
|
|
|
|
reportDashLine(field_description_->width() + field_total_->width() + 1,
|
|
|
|
|
result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportDescription(const char *what,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
result += what;
|
|
|
|
|
int length = strlen(what);
|
|
|
|
|
if (!no_split_ && length > field_description_->width()) {
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
for (int i = 0; i < field_description_->width(); i++)
|
|
|
|
|
result += ' ';
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (int i = length; i < field_description_->width(); i++)
|
|
|
|
|
result += ' ';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2018-11-26 18:15:52 +01:00
|
|
|
ReportPath::reportFieldTime(float value,
|
|
|
|
|
ReportField *field,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
if (delayAsFloat(value) == field_blank_)
|
|
|
|
|
reportFieldBlank(field, result);
|
|
|
|
|
else {
|
|
|
|
|
const char *str = units_->timeUnit()->asString(value, digits_);
|
|
|
|
|
if (stringEq(str, minus_zero_))
|
|
|
|
|
// Filter "-0.00" fields.
|
|
|
|
|
str = plus_zero_;
|
|
|
|
|
reportField(str, field, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportSpaceFieldTime(float value,
|
2018-09-28 17:54:21 +02:00
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
result += ' ';
|
2018-11-26 18:15:52 +01:00
|
|
|
reportFieldTime(value, field_total_, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2018-11-26 18:15:52 +01:00
|
|
|
ReportPath::reportSpaceFieldDelay(Delay value,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
result += ' ';
|
|
|
|
|
reportTotalDelay(value, early_late, result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportTotalDelay(Delay value,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
string &result)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2018-12-05 23:18:41 +01:00
|
|
|
const char *str = delayAsString(value, early_late, units_, digits_);
|
2018-11-26 18:15:52 +01:00
|
|
|
if (stringEq(str, minus_zero_))
|
2018-09-28 17:54:21 +02:00
|
|
|
// Filter "-0.00" fields.
|
2018-11-26 18:15:52 +01:00
|
|
|
str = plus_zero_;
|
|
|
|
|
reportField(str, field_total_, result);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Total time always with leading minus sign.
|
|
|
|
|
void
|
2018-11-26 18:15:52 +01:00
|
|
|
ReportPath::reportFieldDelayMinus(Delay value,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
ReportField *field,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
if (delayAsFloat(value) == field_blank_)
|
|
|
|
|
reportFieldBlank(field, result);
|
|
|
|
|
else {
|
2018-12-05 23:18:41 +01:00
|
|
|
float mean_sigma = delayAsFloat(value, early_late);
|
2018-11-26 18:15:52 +01:00
|
|
|
const char *str = units_->timeUnit()->asString(-mean_sigma, digits_);
|
|
|
|
|
if (stringEq(str, plus_zero_))
|
|
|
|
|
// Force leading minus sign.
|
|
|
|
|
str = minus_zero_;
|
|
|
|
|
reportField(str, field, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFieldDelay(Delay value,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
ReportField *field,
|
|
|
|
|
string &result)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
if (delayAsFloat(value) == field_blank_)
|
|
|
|
|
reportFieldBlank(field, result);
|
|
|
|
|
else {
|
2018-12-05 23:18:41 +01:00
|
|
|
const char *str = delayAsString(value, early_late, units_, digits_);
|
2018-11-26 18:15:52 +01:00
|
|
|
if (stringEq(str, minus_zero_))
|
|
|
|
|
// Filter "-0.00" fields.
|
|
|
|
|
str = plus_zero_;
|
|
|
|
|
reportField(str, field, result);
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportField(float value,
|
|
|
|
|
ReportField *field,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
if (value == field_blank_)
|
|
|
|
|
reportFieldBlank(field, result);
|
|
|
|
|
else {
|
|
|
|
|
const char *value_str = field->unit()->asString(value, digits_);
|
|
|
|
|
reportField(value_str, field, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportField(const char *value,
|
|
|
|
|
ReportField *field,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
if (field->leftJustify())
|
|
|
|
|
result += value;
|
|
|
|
|
for (int i = strlen(value); i < field->width(); i++)
|
|
|
|
|
result += ' ';
|
|
|
|
|
if (!field->leftJustify())
|
|
|
|
|
result += value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportFieldBlank(ReportField *field,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
result += field->blank();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportDashLine(string &result)
|
|
|
|
|
{
|
|
|
|
|
ReportFieldSeq::Iterator field_iter(fields_);
|
|
|
|
|
while (field_iter.hasNext()) {
|
|
|
|
|
ReportField *field = field_iter.next();
|
|
|
|
|
if (field->enabled()) {
|
|
|
|
|
for (int i = 0; i < field->width(); i++)
|
|
|
|
|
result += '-';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result += "------";
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportDashLine(int line_width,
|
|
|
|
|
string &result)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < line_width; i++)
|
|
|
|
|
result += '-';
|
|
|
|
|
reportEndOfLine(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReportPath::reportEndOfLine(string &result)
|
|
|
|
|
{
|
|
|
|
|
result += '\n';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ReportPath::reportClkPath() const
|
|
|
|
|
{
|
|
|
|
|
return format_ == report_path_full_clock
|
|
|
|
|
|| format_ == report_path_full_clock_expanded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::asRisingFalling(const TransRiseFall *tr)
|
|
|
|
|
{
|
|
|
|
|
if (tr == TransRiseFall::rise())
|
|
|
|
|
return "rising";
|
|
|
|
|
else
|
|
|
|
|
return "falling";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::asRiseFall(const TransRiseFall *tr)
|
|
|
|
|
{
|
|
|
|
|
if (tr == TransRiseFall::rise())
|
|
|
|
|
return "rise";
|
|
|
|
|
else
|
|
|
|
|
return "fall";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the startpoint type from the first path edge.
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::edgeRegLatchDesc(Edge *first_edge,
|
|
|
|
|
TimingArc *first_arc)
|
|
|
|
|
{
|
|
|
|
|
TimingRole *role = first_arc->role();
|
|
|
|
|
if (role == TimingRole::latchDtoQ()) {
|
|
|
|
|
Instance *inst = network_->instance(first_edge->to(graph_)->pin());
|
|
|
|
|
LibertyCell *cell = network_->libertyCell(inst);
|
|
|
|
|
if (cell) {
|
|
|
|
|
LibertyPort *enable_port;
|
|
|
|
|
FuncExpr *enable_func;
|
|
|
|
|
TransRiseFall *enable_tr;
|
|
|
|
|
cell->latchEnable(first_edge->timingArcSet(),
|
|
|
|
|
enable_port, enable_func, enable_tr);
|
|
|
|
|
return latchDesc(enable_tr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (role == TimingRole::regClkToQ())
|
|
|
|
|
return regDesc(first_arc->fromTrans()->asRiseFall());
|
|
|
|
|
else if (role == TimingRole::latchEnToQ())
|
|
|
|
|
return latchDesc(first_arc->fromTrans()->asRiseFall());
|
|
|
|
|
// Who knows...
|
|
|
|
|
return regDesc(first_arc->fromTrans()->asRiseFall());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::checkRegLatchDesc(const TimingRole *role,
|
|
|
|
|
const TransRiseFall *clk_tr) const
|
|
|
|
|
{
|
|
|
|
|
if (role == TimingRole::regClkToQ())
|
|
|
|
|
return regDesc(clk_tr);
|
|
|
|
|
else if (role == TimingRole::latchEnToQ()
|
|
|
|
|
|| role == TimingRole::latchDtoQ())
|
|
|
|
|
return latchDesc(clk_tr);
|
|
|
|
|
else
|
|
|
|
|
// Default when we don't know better.
|
|
|
|
|
return "edge-triggered flip-flop";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::regDesc(const TransRiseFall *clk_tr) const
|
|
|
|
|
{
|
|
|
|
|
if (clk_tr == TransRiseFall::rise())
|
|
|
|
|
return "rising edge-triggered flip-flop";
|
|
|
|
|
else if (clk_tr == TransRiseFall::fall())
|
|
|
|
|
return "falling edge-triggered flip-flop";
|
|
|
|
|
else
|
|
|
|
|
return "edge-triggered flip-flop";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ReportPath::latchDesc(const TransRiseFall *clk_tr) const
|
|
|
|
|
{
|
|
|
|
|
return (clk_tr == TransRiseFall::rise())
|
|
|
|
|
? "positive level-sensitive latch"
|
|
|
|
|
: "negative level-sensitive latch";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|