diff --git a/include/sta/ReportFieldExtension.hh b/include/sta/ReportFieldExtension.hh new file mode 100644 index 00000000..159315c2 --- /dev/null +++ b/include/sta/ReportFieldExtension.hh @@ -0,0 +1,57 @@ +// OpenSTA, Static Timing Analyzer +// Copyright (c) 2026, Parallax Software, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. +// +// Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include + +#include "NetworkClass.hh" +#include "SearchClass.hh" + +namespace sta { + +// External producers register extensions to add custom columns to +// report_checks output. ReportPath takes ownership of the registered +// pointer and deletes it at shutdown. +class ReportFieldExtension +{ + public: + virtual ~ReportFieldExtension() = default; + + // Identifier matched against `-fields` tokens. + virtual std::string_view name() const = 0; + // Column header. + virtual std::string_view title() const = 0; + // Column width in characters. + virtual size_t width() const = 0; + + // Per-row value. Return "" to leave the cell blank. + virtual std::string value(const Path *path, + const Pin *pin, + const Instance *inst) const = 0; +}; + +} // namespace sta diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index 5237ec9a..a5f026e7 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -71,6 +71,7 @@ class SearchPred; class Scene; class ClkSkews; class ReportField; +class ReportFieldExtension; class EquivCells; class StaSimObserver; class GraphLoop; @@ -989,7 +990,8 @@ public: bool report_slew, bool report_fanout, bool report_variation, - bool report_src_attr); + bool report_src_attr, + const StringSeq &extension_names); ReportField *findReportPathField(std::string_view name); void setReportPathDigits(int digits); void setReportPathNoSplit(bool no_split); @@ -997,6 +999,9 @@ public: void reportPathEnds(PathEndSeq *ends); ReportPath *reportPath() { return report_path_; } void reportPath(const Path *path); + // Register an extension that adds a custom column to report_checks. + // Sta takes ownership and deletes at destruction. + void registerReportFieldExtension(ReportFieldExtension *ext); // Report clk skews for clks. void reportClkSkew(ConstClockSeq &clks, diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 50d05574..06513108 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -53,6 +53,7 @@ #include "PortDelay.hh" #include "PortDirection.hh" #include "Report.hh" +#include "ReportFieldExtension.hh" #include "Scene.hh" #include "Sdc.hh" #include "Search.hh" @@ -96,7 +97,9 @@ ReportField::ReportField(std::string_view name, } ReportField::~ReportField() -= default; +{ + delete extension_; +} void ReportField::setProperties(std::string_view title, @@ -128,7 +131,8 @@ ReportPath::ReportPath(StaState *sta) : { makeFields(); setDigits(2); - setReportFields(false, false, false, false, false, false, false, false); + setReportFields(false, false, false, false, false, false, false, false, + StringSeq{}); } ReportPath::~ReportPath() @@ -226,7 +230,8 @@ ReportPath::setReportFields(bool report_input_pin, bool report_slew, bool report_fanout, bool report_variation, - bool report_src_attr) + bool report_src_attr, + const StringSeq &extension_names) { report_input_pin_ = report_input_pin; report_hier_pins_ = report_hier_pins; @@ -239,6 +244,62 @@ ReportPath::setReportFields(bool report_input_pin, field_src_attr_->setEnabled(report_src_attr); // for debug field_case_->setEnabled(false); + + // Reset extension-backed fields: disable, drop slot assignment. + for (ReportField *field : fields_) { + if (field->extension() != nullptr) { + field->setEnabled(false); + field->setExtensionIndex(-1); + } + } + // Enable each requested extension name and assign a buffer slot. + int slot = 0; + for (const std::string &name : extension_names) { + ReportField *field = findField(name); + if (field == nullptr || field->extension() == nullptr) { + report_->warn(168, "no field extension registered for '{}'.", name); + continue; + } + field->setEnabled(true); + field->setExtensionIndex(slot++); + } + enabled_extension_count_ = slot; +} + +void +ReportPath::registerFieldExtension(ReportFieldExtension *ext) +{ + if (findField(ext->name()) != nullptr) { + // Already registered (e.g. re-init in a long-lived Sta). Silently dedupe. + delete ext; + return; + } + ReportField *field = makeField(ext->name(), + ext->title(), + static_cast(ext->width()), + true, + nullptr, + false); + field->setExtension(ext); +} + +void +ReportPath::collectExtensionValues(const Path *path, + const Pin *pin, + const Instance *inst, + std::vector &values) const +{ + if (enabled_extension_count_ == 0) { + values.clear(); + return; + } + values.resize(enabled_extension_count_); + for (const ReportField *field : fields_) { + ReportFieldExtension *ext = field->extension(); + if (ext == nullptr || field->extensionIndex() < 0) + continue; + values[field->extensionIndex()] = ext->value(path, pin, inst); + } } void @@ -2451,11 +2512,13 @@ ReportPath::reportPathLine(const Path *path, std::string src_attr; if (inst) src_attr = network_->getAttribute(inst, "src"); + collectExtensionValues(path, pin, inst, extension_values_buf_); // Don't show capacitance field for input pins. if (is_driver && field_capacitance_->enabled()) cap = graph_delay_calc_->loadCap(pin, rf, scene, min_max); reportLine(what, cap, slew, field_blank_, incr, field_blank_, - time, false, early_late, rf, src_attr, line_case); + time, false, early_late, rf, src_attr, line_case, + extension_values_buf_); } void @@ -2733,6 +2796,7 @@ ReportPath::reportPath6(const Path *path, std::string src_attr; if (inst) src_attr = network_->getAttribute(inst, "src"); + collectExtensionValues(path1, pin, inst, extension_values_buf_); // Always show the search start point (register clk pin). // Skip reporting the clk tree unless it is requested. if (is_clk_start @@ -2825,7 +2889,7 @@ ReportPath::reportPath6(const Path *path, const std::string what = descriptionField(vertex); reportLine(what, cap, slew, fanout, incr, field_blank_, time, false, min_max, rf, src_attr, - line_case); + line_case, extension_values_buf_); if (report_net_) { const std::string what2 = descriptionNet(pin); @@ -2844,7 +2908,7 @@ ReportPath::reportPath6(const Path *path, const std::string what = descriptionField(vertex); reportLine(what, field_blank_, slew, field_blank_, incr, field_blank_, time, false, min_max, rf, src_attr, - line_case); + line_case, extension_values_buf_); prev_time = time; } } @@ -3182,7 +3246,8 @@ ReportPath::reportLine(std::string_view what, const EarlyLate *early_late, const RiseFall *rf, std::string_view src_attr, - std::string_view line_case) const + std::string_view line_case, + std::span extension_values) const { std::string line; size_t field_index = 0; @@ -3234,6 +3299,13 @@ ReportPath::reportLine(std::string_view what, } else if (field == field_case_) line += line_case; + else if (field->extensionIndex() >= 0) { + size_t idx = static_cast(field->extensionIndex()); + if (idx < extension_values.size() && !extension_values[idx].empty()) + reportField(extension_values[idx], field, line); + else + reportFieldBlank(field, line); + } first_field = false; } diff --git a/search/ReportPath.hh b/search/ReportPath.hh index b8ce652d..1acb60c7 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include #include @@ -50,6 +51,7 @@ namespace sta { class Scene; class PathExpanded; class ReportField; +class ReportFieldExtension; using ReportFieldSeq = std::vector; @@ -68,7 +70,13 @@ public: bool report_slew, bool report_fanout, bool report_variation, - bool report_src_attr); + bool report_src_attr, + const StringSeq &extension_names); + + // Register an extension that adds a custom column to report_checks. + // ReportPath takes ownership and deletes at destruction. + void registerFieldExtension(ReportFieldExtension *ext); + int digits() const { return digits_; } void setDigits(int digits); void setNoSplit(bool no_split); @@ -280,6 +288,10 @@ protected: const Delay &incr, const Arrival &time, std::string_view line_case) const; + void collectExtensionValues(const Path *path, + const Pin *pin, + const Instance *inst, + std::vector &values) const; void reportCommonClkPessimism(const PathEnd *end, Arrival &clk_arrival) const ; void reportClkUncertainty(const PathEnd *end, @@ -380,7 +392,8 @@ protected: const EarlyLate *early_late, const RiseFall *rf, std::string_view src_attr, - std::string_view line_case) const; + std::string_view line_case, + std::span extension_values = {}) const; void reportLineTotal(std::string_view what, const Delay &incr, const EarlyLate *early_late) const; @@ -502,6 +515,8 @@ protected: ReportField *field_src_attr_; ReportField *field_edge_; ReportField *field_case_; + mutable std::vector extension_values_buf_; + int enabled_extension_count_{0}; std::string plus_zero_; std::string minus_zero_; @@ -533,6 +548,11 @@ public: const std::string &blank() const { return blank_; } void setEnabled(bool enabled); bool enabled() const { return enabled_; } + int extensionIndex() const { return extension_index_; } + void setExtensionIndex(int index) { extension_index_ = index; } + ReportFieldExtension *extension() const { return extension_; } + // ReportField takes ownership of the extension and deletes it. + void setExtension(ReportFieldExtension *ext) { extension_ = ext; } protected: std::string name_; @@ -542,6 +562,8 @@ protected: Unit *unit_; bool enabled_; std::string blank_; + int extension_index_{-1}; + ReportFieldExtension *extension_{nullptr}; }; } // namespace sta diff --git a/search/Search.i b/search/Search.i index c378a5e8..df1b55c8 100644 --- a/search/Search.i +++ b/search/Search.i @@ -414,7 +414,8 @@ set_report_path_fields(bool report_input_pin, bool report_slew, bool report_fanout, bool report_variation, - bool report_src_attr) + bool report_src_attr, + StringSeq extension_names) { Sta::sta()->setReportPathFields(report_input_pin, report_hier_pins, @@ -423,7 +424,8 @@ set_report_path_fields(bool report_input_pin, report_slew, report_fanout, report_variation, - report_src_attr); + report_src_attr, + extension_names); } void diff --git a/search/Search.tcl b/search/Search.tcl index 11394292..e4dd296a 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -336,7 +336,7 @@ define_cmd_args "report_checks" \ [-sort_by_slack]\ [-path_group group_name]\ [-format full|full_clock|full_clock_expanded|short|end|slack_only|summary|json]\ - [-fields capacitance|slew|fanout|input_pin|net|src_attr]\ + [-fields capacitance|slew|fanout|input_pin|net|src_attr|...]\ [-digits digits]\ [-no_line_splits]\ [> filename] [>> filename]} @@ -724,6 +724,7 @@ proc parse_report_path_options { cmd args_var default_format set report_fanout 0 set report_variation 0 set report_src_attr 0 + set extension_names {} if { [info exists path_options(-fields)] } { foreach field $path_options(-fields) { if { [string match "input*" $field] } { @@ -743,13 +744,14 @@ proc parse_report_path_options { cmd args_var default_format } elseif { [string match "src*" $field] } { set report_src_attr 1 } else { - sta_warn 168 "unknown field $field." + lappend extension_names $field } } } set_report_path_fields $report_input_pin $report_hier_pins $report_net \ - $report_cap $report_slew $report_fanout $report_variation $report_src_attr + $report_cap $report_slew $report_fanout $report_variation $report_src_attr \ + $extension_names set_report_path_no_split [info exists path_options(-no_line_splits)] } diff --git a/search/Sta.cc b/search/Sta.cc index c5a40139..27798c26 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2823,11 +2823,19 @@ Sta::setReportPathFields(bool report_input_pin, bool report_slew, bool report_fanout, bool report_variation, - bool report_src_attr) + bool report_src_attr, + const StringSeq &extension_names) { report_path_->setReportFields(report_input_pin, report_hier_pins, report_net, report_cap, report_slew, report_fanout, - report_variation, report_src_attr); + report_variation, report_src_attr, + extension_names); +} + +void +Sta::registerReportFieldExtension(ReportFieldExtension *ext) +{ + report_path_->registerFieldExtension(ext); } ReportField *