OpenSTA/sdf/ReportAnnotation.cc

538 lines
16 KiB
C++

// OpenSTA, Static Timing Analyzer
// Copyright (c) 2025, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
#include "sdf/ReportAnnotation.hh"
#include "StringUtil.hh"
#include "Report.hh"
#include "TimingRole.hh"
#include "TimingArc.hh"
#include "Liberty.hh"
#include "Network.hh"
#include "Graph.hh"
#include "GraphCmp.hh"
#include "Sdc.hh"
#include "DcalcAnalysisPt.hh"
namespace sta {
class ReportAnnotated : public StaState
{
public:
ReportAnnotated(bool report_cells,
bool report_nets,
bool report_in_ports,
bool report_out_ports,
int max_lines,
bool list_annotated,
bool list_unannotated,
bool constant_arcs,
StaState *sta);
ReportAnnotated(bool report_setup,
bool report_hold,
bool report_recovery,
bool report_removal,
bool report_nochange,
bool report_width,
bool report_period,
bool report_max_skew,
int max_lines,
bool list_annotated,
bool list_unannotated,
bool constant_arcs,
StaState *sta);
void reportDelayAnnotation();
void reportCheckAnnotation();
protected:
enum CountIndex {
count_internal_net = TimingRole::index_max,
count_input_net,
count_output_net,
count_index_max
};
static int count_delay;
void init();
void findCounts();
void findPeriodCount(Pin *pin);
void reportDelayCounts();
void reportCheckCounts();
void reportArcs();
void reportArcs(const char *header,
bool report_annotated,
PinSet &pins);
void reportArcs(Vertex *vertex,
bool report_annotated,
int &i);
void reportPeriodArcs(const Pin *pin,
bool report_annotated,
int &i);
void reportCount(const char *title,
int index,
int &total,
int &annotated_total);
void reportCheckCount(const TimingRole *role,
int &total,
int &annotated_total);
int roleIndex(const TimingRole *role,
const Pin *from_pin,
const Pin *to_pin);
int max_lines_;
bool list_annotated_;
bool list_unannotated_;
bool report_constant_arcs_;
int edge_count_[count_index_max];
int edge_annotated_count_[count_index_max];
int edge_constant_count_[count_index_max];
int edge_constant_annotated_count_[count_index_max];
bool report_role_[count_index_max];
PinSet unannotated_pins_;
PinSet annotated_pins_;
};
int ReportAnnotated::count_delay;
void
reportAnnotatedDelay(bool report_cells,
bool report_nets,
bool from_in_ports,
bool to_out_ports,
int max_lines,
bool list_annotated,
bool list_unannotated,
bool report_constant_arcs,
StaState *sta)
{
ReportAnnotated report_annotated(report_cells, report_nets,
from_in_ports, to_out_ports,
max_lines, list_annotated, list_unannotated,
report_constant_arcs, sta);
report_annotated.reportDelayAnnotation();
}
ReportAnnotated::ReportAnnotated(bool report_cells,
bool report_nets,
bool report_in_ports,
bool report_out_ports,
int max_lines,
bool list_annotated,
bool list_unannotated,
bool report_constant_arcs,
StaState *sta) :
StaState(sta),
max_lines_(max_lines),
list_annotated_(list_annotated),
list_unannotated_(list_unannotated),
report_constant_arcs_(report_constant_arcs),
unannotated_pins_(sta->network()),
annotated_pins_(sta->network())
{
init();
report_role_[TimingRole::sdfIopath()->index()] = report_cells;
report_role_[count_internal_net] = report_nets;
report_role_[count_input_net] = report_in_ports;
report_role_[count_output_net] = report_out_ports;
}
void
ReportAnnotated::reportDelayAnnotation()
{
findCounts();
reportDelayCounts();
reportArcs();
}
void
ReportAnnotated::reportDelayCounts()
{
report_->reportLine(" Not ");
report_->reportLine("Delay type Total Annotated Annotated");
report_->reportLine("----------------------------------------------------------------");
int total = 0;
int annotated_total = 0;
reportCount("cell arcs", count_delay, total, annotated_total);
reportCount("internal net arcs", count_internal_net, total, annotated_total);
reportCount("net arcs from primary inputs", count_input_net,
total, annotated_total);
reportCount("net arcs to primary outputs", count_output_net,
total, annotated_total);
report_->reportLine("----------------------------------------------------------------");
report_->reportLine("%-28s %10u %10u %10u",
" ",
total,
annotated_total,
total - annotated_total);
}
////////////////////////////////////////////////////////////////
void
reportAnnotatedCheck(bool report_setup,
bool report_hold,
bool report_recovery,
bool report_removal,
bool report_nochange,
bool report_width,
bool report_period,
bool report_max_skew,
int max_lines,
bool list_annotated,
bool list_unannotated,
bool report_constant_arcs,
StaState *sta)
{
ReportAnnotated report_annotated(report_setup, report_hold,
report_recovery, report_removal,
report_nochange, report_width,
report_period, report_max_skew,
max_lines, list_annotated, list_unannotated,
report_constant_arcs, sta);
report_annotated.reportCheckAnnotation();
}
ReportAnnotated::ReportAnnotated(bool report_setup,
bool report_hold,
bool report_recovery,
bool report_removal,
bool report_nochange,
bool report_width,
bool report_period,
bool report_max_skew,
int max_lines,
bool list_annotated,
bool list_unannotated,
bool report_constant_arcs,
StaState *sta) :
StaState(sta),
max_lines_(max_lines),
list_annotated_(list_annotated),
list_unannotated_(list_unannotated),
report_constant_arcs_(report_constant_arcs),
unannotated_pins_(sta->network()),
annotated_pins_(sta->network())
{
init();
report_role_[TimingRole::setup()->index()] = report_setup;
report_role_[TimingRole::hold()->index()] = report_hold;
report_role_[TimingRole::recovery()->index()] = report_recovery;
report_role_[TimingRole::removal()->index()] = report_removal;
report_role_[TimingRole::nochange()->index()] = report_nochange;
report_role_[TimingRole::width()->index()] = report_width;
report_role_[TimingRole::period()->index()] = report_period;
report_role_[TimingRole::skew()->index()] = report_max_skew;
}
void
ReportAnnotated::reportCheckAnnotation()
{
findCounts();
reportCheckCounts();
reportArcs();
}
void
ReportAnnotated::reportCheckCounts()
{
report_->reportLine(" Not ");
report_->reportLine("Check type Total Annotated Annotated");
report_->reportLine("----------------------------------------------------------------");
int total = 0;
int annotated_total = 0;
reportCheckCount(TimingRole::setup(), total, annotated_total);
reportCheckCount(TimingRole::hold(), total, annotated_total);
reportCheckCount(TimingRole::recovery(), total, annotated_total);
reportCheckCount(TimingRole::removal(), total, annotated_total);
reportCheckCount(TimingRole::nochange(), total, annotated_total);
reportCheckCount(TimingRole::width(), total, annotated_total);
reportCheckCount(TimingRole::period(), total, annotated_total);
reportCheckCount(TimingRole::skew(), total, annotated_total);
report_->reportLine("----------------------------------------------------------------");
report_->reportLine("%-28s %10u %10u %10u",
" ",
total,
annotated_total,
total - annotated_total);
}
void
ReportAnnotated::reportCheckCount(const TimingRole *role,
int &total,
int &annotated_total)
{
int index = role->index();
if (edge_count_[index] > 0) {
std::string title;
stringPrint(title, "cell %s arcs", role->to_string().c_str());
reportCount(title.c_str(), index, total, annotated_total);
}
}
////////////////////////////////////////////////////////////////
void
ReportAnnotated::init()
{
count_delay = TimingRole::sdfIopath()->index();
for (int i = 0; i < count_index_max; i++) {
edge_count_[i] = 0;
edge_annotated_count_[i] = 0;
edge_constant_count_[i] = 0;
edge_constant_annotated_count_[i] = 0;
report_role_[i] = false;
}
}
void
ReportAnnotated::findCounts()
{
VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) {
Vertex *from_vertex = vertex_iter.next();
Pin *from_pin = from_vertex->pin();
LogicValue from_logic_value;
bool from_logic_value_exists;
sdc_->logicValue(from_pin, from_logic_value,
from_logic_value_exists);
VertexOutEdgeIterator edge_iter(from_vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
const TimingRole *role = edge->role();
Vertex *to_vertex = edge->to(graph_);
Pin *to_pin = to_vertex->pin();
int index = roleIndex(role, from_pin, to_pin);
LogicValue to_logic_value;
bool to_logic_value_exists;
sdc_->logicValue(to_pin, to_logic_value,
to_logic_value_exists);
edge_count_[index]++;
if (from_logic_value_exists || to_logic_value_exists)
edge_constant_count_[index]++;
if (report_role_[index]) {
if (graph_->delayAnnotated(edge)) {
edge_annotated_count_[index]++;
if (from_logic_value_exists || to_logic_value_exists)
edge_constant_annotated_count_[index]++;
if (list_annotated_)
annotated_pins_.insert(from_pin);
}
else {
if (list_unannotated_)
unannotated_pins_.insert(from_pin);
}
}
}
findPeriodCount(from_pin);
}
}
int
ReportAnnotated::roleIndex(const TimingRole *role,
const Pin *from_pin,
const Pin *to_pin)
{
if (role == TimingRole::wire()) {
if (network_->isTopLevelPort(from_pin))
return count_input_net;
else if (network_->isTopLevelPort(to_pin))
return count_output_net;
else
return count_internal_net;
}
else if (role->sdfRole() == TimingRole::sdfIopath())
return count_delay;
else {
if (role->isTimingCheck()
&& (role == TimingRole::latchSetup()
|| role == TimingRole::latchHold()))
role = role->genericRole();
return role->index();
}
}
// Width and period checks are not edges in the graph so
// they require special handling.
void
ReportAnnotated::findPeriodCount(Pin *pin)
{
LibertyPort *port = network_->libertyPort(pin);
if (port) {
DcalcAPIndex ap_index = 0;
float value;
bool exists, annotated;
int period_index = TimingRole::period()->index();
if (report_role_[period_index]) {
port->minPeriod(value, exists);
if (exists) {
edge_count_[period_index]++;
graph_->periodCheckAnnotation(pin, ap_index, value, annotated);
if (annotated) {
edge_annotated_count_[period_index]++;
if (list_annotated_)
annotated_pins_.insert(pin);
}
else {
if (list_unannotated_)
unannotated_pins_.insert(pin);
}
}
}
}
}
void
ReportAnnotated::reportCount(const char *title,
int index,
int &total,
int &annotated_total)
{
if (report_role_[index]) {
int count = edge_count_[index];
int annotated_count = edge_annotated_count_[index];
report_->reportLine("%-28s %10u %10u %10u",
title,
count,
annotated_count,
count - annotated_count);
if (report_constant_arcs_) {
int const_count = edge_constant_count_[index];
int const_annotated_count = edge_constant_annotated_count_[index];
report_->reportLine("%-28s %10s %10u %10u",
"constant arcs",
"",
const_annotated_count,
const_count - const_annotated_count);
}
total += count;
annotated_total += annotated_count;
}
}
void
ReportAnnotated::reportArcs()
{
if (list_annotated_)
reportArcs("Annotated Arcs", true, annotated_pins_);
if (list_unannotated_)
reportArcs("Unannotated Arcs", false, unannotated_pins_);
}
void
ReportAnnotated::reportArcs(const char *header,
bool report_annotated,
PinSet &pins)
{
report_->reportBlankLine();
report_->reportLineString(header);
PinSeq pins1 = sortByPathName(&pins, network_);
int i = 0;
for (const Pin *pin : pins1) {
Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
reportArcs(vertex, report_annotated, i);
if (bidirect_drvr_vertex)
reportArcs(bidirect_drvr_vertex, report_annotated, i);
reportPeriodArcs(pin, report_annotated, i);
if (max_lines_ != 0 && i > max_lines_)
break;
}
}
void
ReportAnnotated::reportArcs(Vertex *vertex,
bool report_annotated,
int &i)
{
const Pin *from_pin = vertex->pin();
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()
&& (max_lines_ == 0 || i < max_lines_)) {
Edge *edge = edge_iter.next();
const TimingRole *role = edge->role();
const Pin *to_pin = edge->to(graph_)->pin();
if (graph_->delayAnnotated(edge) == report_annotated
&& report_role_[roleIndex(role, from_pin, to_pin)]) {
const char *role_name;
if (role->isTimingCheck())
role_name = role->to_string().c_str();
else if (role->isWire()) {
if (network_->isTopLevelPort(from_pin))
role_name = "primary input net";
else if (network_->isTopLevelPort(to_pin))
role_name = "primary output net";
else
role_name = "internal net";
}
else
role_name = "delay";
const char *cond = edge->timingArcSet()->sdfCond();
report_->reportLine(" %-18s %s -> %s %s",
role_name,
network_->pathName(from_pin),
network_->pathName(to_pin),
cond ? cond : "");
i++;
}
}
}
void
ReportAnnotated::reportPeriodArcs(const Pin *pin,
bool report_annotated,
int &i)
{
LibertyPort *port = network_->libertyPort(pin);
if (port) {
DcalcAPIndex ap_index = 0;
int period_index = TimingRole::period()->index();
if (report_role_[period_index]
&& (max_lines_ == 0 || i < max_lines_)) {
float value;
bool exists, annotated;
port->minPeriod(value, exists);
if (exists) {
edge_count_[period_index]++;
graph_->periodCheckAnnotation(pin, ap_index, value, annotated);
if (annotated == report_annotated) {
report_->reportLine(" %-18s %s",
"period",
network_->pathName(pin));
i++;
}
}
}
}
}
} // namespace