From 76c6c4ecd91805e395541246cbba9b3e4c18fb20 Mon Sep 17 00:00:00 2001 From: Natalia Date: Mon, 24 Nov 2025 02:16:29 -0800 Subject: [PATCH] Deduplicate by word in report paths --- include/sta/SearchClass.hh | 5 ++ include/sta/Sta.hh | 10 ++-- search/ReportPath.cc | 120 ++++++++++++++++++++++++++++++++++--- search/ReportPath.hh | 14 +++-- search/Search.i | 15 ++++- search/Search.tcl | 12 +++- search/Sta.cc | 18 +++++- tcl/StaTclTypes.i | 16 +++++ test/path_dedup_worst.ok | 3 + test/path_dedup_worst.tcl | 40 +++++++++++++ test/regression_vars.tcl | 3 + 11 files changed, 232 insertions(+), 24 deletions(-) create mode 100644 test/path_dedup_worst.ok create mode 100644 test/path_dedup_worst.tcl diff --git a/include/sta/SearchClass.hh b/include/sta/SearchClass.hh index d9268b946..d596fb170 100644 --- a/include/sta/SearchClass.hh +++ b/include/sta/SearchClass.hh @@ -131,6 +131,11 @@ enum class ReportPathFormat { full, json }; +enum class ReportDeduplicationMode { none, + keep_worst, + keep_different +}; + static const TagIndex tag_index_bit_count = 28; static const TagIndex tag_index_max = (1 << tag_index_bit_count) - 1; static const TagIndex tag_index_null = tag_index_max; diff --git a/include/sta/Sta.hh b/include/sta/Sta.hh index f7b233da4..38179681f 100644 --- a/include/sta/Sta.hh +++ b/include/sta/Sta.hh @@ -857,16 +857,18 @@ public: void setReportPathDigits(int digits); void setReportPathNoSplit(bool no_split); void setReportPathSigmas(bool report_sigmas); + void setReportDedupByWord(bool dedup_by_word); + void setReportDeduplicationMode(ReportDeduplicationMode dedup_mode); // Header above reportPathEnd results. void reportPathEndHeader(); // Footer below reportPathEnd results. void reportPathEndFooter(); // Format report_path_endpoint only: - // Previous path end is used to detect path group changes - // so headers are reported by group. + // Previous path end is used to: + // - detect path group changes so headers are reported by group. + // - JSON format: if not first, add a comma before appending new path void reportPathEnd(PathEnd *end, - PathEnd *prev_end, - bool last); + PathEnd *prev_end); void reportPathEnd(PathEnd *end); void reportPathEnds(PathEndSeq *ends); ReportPath *reportPath() { return report_path_; } diff --git a/search/ReportPath.cc b/search/ReportPath.cc index 6b991557d..4fb6ad2bc 100644 --- a/search/ReportPath.cc +++ b/search/ReportPath.cc @@ -48,6 +48,7 @@ #include "GraphDelayCalc.hh" #include "ClkInfo.hh" #include "Tag.hh" +#include "ParseBus.hh" #include "PathAnalysisPt.hh" #include "PathGroup.hh" #include "CheckMinPulseWidths.hh" @@ -61,6 +62,8 @@ #include "Genclks.hh" #include "Variables.hh" +#include + namespace sta { using std::string; @@ -140,6 +143,8 @@ const float ReportPath::field_blank_ = -1.0; ReportPath::ReportPath(StaState *sta) : StaState(sta), format_(ReportPathFormat::full), + dedup_by_word_(false), + report_dedup_mode_(ReportDeduplicationMode::none), no_split_(false), report_sigmas_(false), start_end_pt_width_(80), @@ -290,18 +295,26 @@ ReportPath::setReportSigmas(bool report) report_sigmas_ = report; } +void +ReportPath::setReportDedupByWord(bool dedup_by_word) +{ + dedup_by_word_ = dedup_by_word; +ReportPath::setReportDeduplicationMode(ReportDeduplicationMode dedup_mode) +{ + report_dedup_mode_ = dedup_mode; +} + //////////////////////////////////////////////////////////////// void ReportPath::reportPathEnd(const PathEnd *end) const { - reportPathEnd(end, nullptr, true); + reportPathEnd(end, nullptr); } void ReportPath::reportPathEnd(const PathEnd *end, - const PathEnd *prev_end, - bool last) const + const PathEnd *prev_end) const { switch (format_) { case ReportPathFormat::full: @@ -327,11 +340,34 @@ ReportPath::reportPathEnd(const PathEnd *end, reportSlackOnly(end); break; case ReportPathFormat::json: - reportJson(end, last); + reportJson(end, prev_end); break; } } +inline std::string getBusName(const StaState *state, + const sta::Network *sdc_network, + PathEnd *end) { + char escape = sdc_network->pathEscape(); + PathExpanded expanded(end->path(), state); + const Pin *end_pin = expanded.endPath()->vertex(state)->pin(); + const char *endpoint_name = sdc_network->pathName(end_pin); + bool is_bus; + std::string bus_name; + int index; + parseBusName(endpoint_name, + '[', + ']', + escape, + is_bus, + bus_name, + index); + if (is_bus) { + return bus_name; + } + return ""; +} + void ReportPath::reportPathEnds(const PathEndSeq *ends) const { @@ -339,11 +375,78 @@ ReportPath::reportPathEnds(const PathEndSeq *ends) const if (ends && !ends->empty()) { PathEnd *prev_end = nullptr; PathEndSeq::ConstIterator end_iter(ends); + Set qualified_ends; + + if (dedup_by_word_) { + Map worst_slack_by_bus; + while (end_iter.hasNext()) { + PathEnd *end = end_iter.next(); + auto bus_name = getBusName(this, sdc_network_, end); + if (bus_name.length()) { + if (worst_slack_by_bus.count(bus_name) == 0 || + worst_slack_by_bus[bus_name]->slack(this) > end->slack(this)) + worst_slack_by_bus[bus_name] = end; + } + else + qualified_ends.insert(end); + } + + for (auto &[_, end]: worst_slack_by_bus) + qualified_ends.insert(end); + } + + end_iter = ends; + while (end_iter.hasNext()) { + PathEnd *end = end_iter.next(); + if (!dedup_by_word_ || qualified_ends.count(end)) { + reportPathEnd(end, prev_end); + prev_end = end; + } + char escape = sdc_network_->pathEscape(); + + PathEndSeq qualified_ends; + + // Keep branch/allocation penalties outside the loop + if (report_dedup_mode_ == ReportDeduplicationMode::keep_worst) { + Map worst_slack_by_bus; + + while (end_iter.hasNext()) { + PathEnd *end = end_iter.next(); + PathExpanded expanded(end->path(), this); + const Pin *end_pin = expanded.endPath()->vertex(this)->pin(); + const char *endpoint_name = sdc_network_->pathName(end_pin); + bool is_bus; + std::string bus_name; + int index; + parseBusName( + endpoint_name, + '[', + ']', + escape, + is_bus, + bus_name, + index + ); + if (!is_bus) { + qualified_ends.push_back(end); + } else { + if (worst_slack_by_bus.count(bus_name) == 0 || worst_slack_by_bus[bus_name]->slack(this) > end->slack(this)) { + worst_slack_by_bus[bus_name] = end; + } + } + } + for (auto &[_, end]: worst_slack_by_bus) { + qualified_ends.push_back(end); + } + end_iter = qualified_ends; + } else if (report_dedup_mode_ == ReportDeduplicationMode::keep_different) { + // TODO + } while (end_iter.hasNext()) { PathEnd *end = end_iter.next(); reportPathEnd(end, prev_end, !end_iter.hasNext()); prev_end = end; - } + } } else { if (format_ != ReportPathFormat::json) @@ -1080,9 +1183,12 @@ ReportPath::reportJsonFooter() const void ReportPath::reportJson(const PathEnd *end, - bool last) const + const PathEnd *prev_end) const { string result; + if (prev_end) { + result += ", "; + } result += "{\n"; stringAppend(result, " \"type\": \"%s\",\n", end->typeName()); stringAppend(result, " \"path_group\": \"%s\",\n", @@ -1145,8 +1251,6 @@ ReportPath::reportJson(const PathEnd *end, delayAsFloat(end->slack(this))); } result += "}"; - if (!last) - result += ","; report_->reportLineString(result); } diff --git a/search/ReportPath.hh b/search/ReportPath.hh index b9f4364b2..a0a20aa7e 100644 --- a/search/ReportPath.hh +++ b/search/ReportPath.hh @@ -59,6 +59,8 @@ public: void setNoSplit(bool no_split); bool reportSigmas() const { return report_sigmas_; } void setReportSigmas(bool report); + void setReportDedupByWord(bool dedup_by_word); + void setReportDeduplicationMode(ReportDeduplicationMode report_dedup); ReportField *findField(const char *name) const; // Header above reportPathEnd results. @@ -67,11 +69,11 @@ public: void reportPathEndFooter() const; void reportPathEnd(const PathEnd *end) const; // Format report_path_endpoint only: - // Previous path end is used to detect path group changes - // so headers are reported by group. + // Previous path end is used to: + // - detect path group changes so headers are reported by group. + // - JSON format: if not first, add a comma before appending new path void reportPathEnd(const PathEnd *end, - const PathEnd *prev_end, - bool last) const; + const PathEnd *prev_end) const; void reportPathEnds(const PathEndSeq *ends) const; void reportPath(const Path *path) const; @@ -94,7 +96,7 @@ public: void reportJsonHeader() const; void reportJsonFooter() const; void reportJson(const PathEnd *end, - bool last) const; + const PathEnd *prev_end) const; void reportJson(const Path *path) const; void reportJson(const Path *path, const char *path_name, @@ -472,6 +474,8 @@ protected: bool report_input_pin_; bool report_hier_pins_; bool report_net_; + bool dedup_by_word_; + ReportDeduplicationMode report_dedup_mode_; bool no_split_; int digits_; bool report_sigmas_; diff --git a/search/Search.i b/search/Search.i index 3e2e761aa..3ea325b10 100644 --- a/search/Search.i +++ b/search/Search.i @@ -389,6 +389,7 @@ find_path_ends(ExceptionFrom *from, corner, delay_min_max, group_path_count, endpoint_path_count, unique_pins, unique_edges, + unique_pins, slack_min, slack_max, sort_by_slack, groups->size() ? groups : nullptr, @@ -421,10 +422,9 @@ report_path_end(PathEnd *end) void report_path_end2(PathEnd *end, - PathEnd *prev_end, - bool last) + PathEnd *prev_end) { - Sta::sta()->reportPathEnd(end, prev_end, last); + Sta::sta()->reportPathEnd(end, prev_end); } void @@ -496,6 +496,15 @@ set_report_path_no_split(bool no_split) Sta::sta()->setReportPathNoSplit(no_split); } +void +set_report_path_dedup_by_word(bool dedup_by_word) +{ + Sta::sta()->setReportDedupByWord(dedup_by_word); +set_report_path_deduplication_mode(ReportDeduplicationMode dedup_mode) +{ + Sta::sta()->setReportDeduplicationMode(dedup_mode); +} + void set_report_path_sigmas(bool report_sigmas) { diff --git a/search/Search.tcl b/search/Search.tcl index ba5acba48..5173a53f9 100644 --- a/search/Search.tcl +++ b/search/Search.tcl @@ -429,6 +429,8 @@ define_cmd_args "report_checks" \ [-fields capacitance|slew|fanout|input_pin|net|src_attr]\ [-digits digits]\ [-no_line_splits]\ + [-dedup_by_word]\ + [-deduplication_mode none|keep_worst|keep_different]\ [> filename] [>> filename]} proc_redirect report_checks { @@ -886,8 +888,9 @@ proc parse_report_path_options { cmd args_var default_format if [info exists path_options] { unset path_options } - parse_key_args $cmd args path_options {-format -digits -fields} \ + parse_key_args $cmd args path_options {-format -digits -fields -deduplication_mode} \ path_options {-no_line_splits -report_sigmas} $unknown_key_is_error + path_options {-no_line_splits -report_sigmas -dedup_by_word} $unknown_key_is_error set format $default_format if [info exists path_options(-format)] { @@ -959,6 +962,13 @@ proc parse_report_path_options { cmd args_var default_format $report_cap $report_slew $report_fanout $report_src_attr set_report_path_no_split [info exists path_options(-no_line_splits)] + + if { [info exists path_options(-dedup_by_word)] } { + set_report_path_dedup_by_word 1 + + if { [info exists path_options(-deduplication_mode)] } { + set_report_path_deduplication_mode $path_options(-deduplication_mode) + } } ################################################################ diff --git a/search/Sta.cc b/search/Sta.cc index 99675388a..d8eee3266 100644 --- a/search/Sta.cc +++ b/search/Sta.cc @@ -2564,6 +2564,19 @@ Sta::setReportPathSigmas(bool report_sigmas) } void +Sta::setReportDedupByWord(bool dedup_by_word) +{ + report_path_->setReportDedupByWord(dedup_by_word); +} + +void +Sta::setReportDeduplicationMode(ReportDeduplicationMode dedup_mode) +{ + report_path_->setReportDeduplicationMode(dedup_mode); +} + +void +>>>>>>> 6b6c046e (feat: bus de-duplication in reports [WIP]) Sta::reportPathEndHeader() { report_path_->reportPathEndHeader(); @@ -2583,10 +2596,9 @@ Sta::reportPathEnd(PathEnd *end) void Sta::reportPathEnd(PathEnd *end, - PathEnd *prev_end, - bool last) + PathEnd *prev_end) { - report_path_->reportPathEnd(end, prev_end, last); + report_path_->reportPathEnd(end, prev_end); } void diff --git a/tcl/StaTclTypes.i b/tcl/StaTclTypes.i index f4c0e3841..89c37bba2 100644 --- a/tcl/StaTclTypes.i +++ b/tcl/StaTclTypes.i @@ -1057,6 +1057,22 @@ using namespace sta; } } +%typemap(in) ReportDeduplicationMode { + int length; + char *arg = Tcl_GetStringFromObj($input, &length); + if (stringEq(arg, "none")) + $1 = ReportDeduplicationMode::none; + else if (stringEq(arg, "keep_worst")) + $1 = ReportDeduplicationMode::keep_worst; + else if (stringEq(arg, "keep_different")) + $1 = ReportDeduplicationMode::keep_different; + else { + // Issue here: sometime during the sprintf arg becomes garbage + tclArgError(interp, 2172, "unknown report word deduplication mode %s.", arg); + return TCL_ERROR; + } +} + %typemap(in) ExceptionThruSeq* { $1 = tclListSeqPtr($input, SWIGTYPE_p_ExceptionThru, interp); } diff --git a/test/path_dedup_worst.ok b/test/path_dedup_worst.ok new file mode 100644 index 000000000..e57c758a7 --- /dev/null +++ b/test/path_dedup_worst.ok @@ -0,0 +1,3 @@ +Warning: ../examples/gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. +resp_msg count: 1 +resp_msg[15] count: 1 diff --git a/test/path_dedup_worst.tcl b/test/path_dedup_worst.tcl new file mode 100644 index 000000000..87e4e6027 --- /dev/null +++ b/test/path_dedup_worst.tcl @@ -0,0 +1,40 @@ +read_liberty ../examples/sky130hd_tt.lib.gz +read_verilog ../examples/gcd_sky130hd.v +link_design gcd +read_sdc ../examples/gcd_sky130hd.sdc + +proc count_pattern {pattern filename} { + set count 0 + set fh [open $filename r] + while {[gets $fh line] >= 0} { + if {[regexp $pattern $line]} { + incr count + } + } + close $fh + return $count +} + +proc make_checks_rpt {args} { + set temp_file_id [file tempfile temp_filename] + close $temp_file_id + + report_checks\ + -format end\ + {*}$args > $temp_filename + + return $temp_filename +} + +set checks_rpt [make_checks_rpt\ + -group_path_count 999\ + -fields {input_pins slew}\ + -dedup_by_word] + +puts "resp_msg count: [count_pattern "resp_msg" $checks_rpt]" +puts "resp_msg[15] count: [count_pattern "resp_msg\\\[15\\\]" $checks_rpt]" + -deduplication_mode keep_worst] + +puts "resp_msg count: [count_pattern "resp_msg" $checks_rpt]\nresp_msg[15] count: [count_pattern "resp_msg\\\[15\\\]" $checks_rpt]" + +file delete -force $checks_rpt diff --git a/test/regression_vars.tcl b/test/regression_vars.tcl index f3eb42e97..1881c84f7 100644 --- a/test/regression_vars.tcl +++ b/test/regression_vars.tcl @@ -153,6 +153,9 @@ record_sta_tests { liberty_latch3 package_require path_group_names + path_dedup_different + path_dedup_worst + pin_props prima3 report_checks_sorted report_checks_src_attr