// OpenSTA, Static Timing Analyzer // Copyright (c) 2024, 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 . #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(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(TimingRole *role, int &total, int &annotated_total) { int index = role->index(); if (edge_count_[index] > 0) { string title; stringPrint(title, "cell %s arcs", role->asString()); 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(); 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->asString(); 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