// 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 . // // 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 "Tag.hh" #include "Report.hh" #include "Network.hh" #include "Clock.hh" #include "PortDelay.hh" #include "ExceptionPath.hh" #include "Sdc.hh" #include "Graph.hh" #include "Corner.hh" #include "Search.hh" #include "PathAnalysisPt.hh" #include "ClkInfo.hh" namespace sta { Tag::Tag(TagIndex index, int rf_index, PathAPIndex path_ap_index, const ClkInfo *clk_info, bool is_clk, InputDelay *input_delay, bool is_segment_start, ExceptionStateSet *states, bool own_states, const StaState *sta) : clk_info_(clk_info), input_delay_(input_delay), states_(states), index_(index), is_clk_(is_clk), is_filter_(false), is_loop_(false), is_segment_start_(is_segment_start), own_states_(own_states), rf_index_(rf_index), path_ap_index_(path_ap_index) { findHash(); if (states_) { FilterPath *filter = sta->search()->filter(); for (ExceptionState *state : *states_) { ExceptionPath *exception = state->exception(); if (exception->isLoop()) is_loop_ = true; if (exception == filter) is_filter_ = true; } } } Tag::~Tag() { if (own_states_ && states_) delete states_; } std::string Tag::to_string(const StaState *sta) const { return to_string(true, true, sta); } std::string Tag::to_string(bool report_index, bool report_rf_min_max, const StaState *sta) const { const Network *network = sta->network(); const Corners *corners = sta->corners(); std::string result; if (report_index) result += std::to_string(index_); if (report_rf_min_max) { const RiseFall *rf = transition(); PathAnalysisPt *path_ap = corners->findPathAnalysisPt(path_ap_index_); result += " "; result += rf->to_string().c_str(); result += " "; result += path_ap->pathMinMax()->to_string(); result += "/"; result += std::to_string(path_ap_index_); } result += " "; const ClockEdge *clk_edge = clkEdge(); if (clk_edge) result += clk_edge->name(); else result += "unclocked"; bool is_genclk_src = clk_info_->isGenClkSrcPath(); if (is_clk_ || is_genclk_src) { result += " ("; if (is_clk_) { result += "clock"; if (clk_info_->isPropagated()) result += " prop"; else result += " ideal"; if (is_genclk_src) result += " "; } if (clk_info_->isGenClkSrcPath()) result += "genclk"; result += ")"; } const Pin *clk_src = clkSrc(); if (clk_src) { result += " clk_src "; result += network->pathName(clk_src); } result += " crpr_pin "; const Path *crpr_clk_path = clk_info_->crprClkPath(sta); if (crpr_clk_path) { result += network->pathName(crpr_clk_path->pin(sta)); result += " "; result += crpr_clk_path->minMax(sta)->to_string(); } else result += "null"; if (input_delay_) { result += " input "; result += network->pathName(input_delay_->pin()); } if (is_segment_start_) result += " segment_start"; if (states_) { for (ExceptionState *state : *states_) { ExceptionPath *exception = state->exception(); result += " "; result += exception->asString(network); if (state->nextThru()) { result += " (next thru "; result += state->nextThru()->asString(network); result += ")"; } else { if (exception->thrus() != nullptr) result += " (thrus complete)"; } } } return result; } const RiseFall * Tag::transition() const { return RiseFall::find(rf_index_); } const MinMax * Tag::minMax(const StaState *sta) const { return pathAnalysisPt(sta)->pathMinMax(); } PathAnalysisPt * Tag::pathAnalysisPt(const StaState *sta) const { const Corners *corners = sta->corners(); return corners->findPathAnalysisPt(path_ap_index_); } void Tag::setStates(ExceptionStateSet *states) { states_ = states; } const ClockEdge * Tag::clkEdge() const { return clk_info_->clkEdge(); } const Clock * Tag::clock() const { return clk_info_->clock(); } const Pin * Tag::clkSrc() const { return clk_info_->clkSrc(); } bool Tag::isGenClkSrcPath() const { return clk_info_->isGenClkSrcPath(); } const Clock * Tag::genClkSrcPathClk(const StaState *sta) const { if (clk_info_->isGenClkSrcPath() && states_) { FilterPath *filter = sta->search()->filter(); for (ExceptionState *state : *states_) { ExceptionPath *except = state->exception(); if (except->isFilter() && except != filter) { ExceptionTo *to = except->to(); if (to) { ClockSet *clks = to->clks(); if (clks && clks->size() == 1) { ClockSet::Iterator clk_iter(clks); const Clock *clk = clk_iter.next(); return clk; } } } } } return nullptr; } void Tag::findHash() { // Common to hash_ and match_hash_. hash_ = hash_init_value; hashIncr(hash_, rf_index_); hashIncr(hash_, path_ap_index_); hashIncr(hash_, is_clk_); hashIncr(hash_, is_segment_start_); if (states_) { for (ExceptionState *state : *states_) hashIncr(hash_, state->hash()); } hashIncr(hash_, clk_info_->hash()); match_hash_ = hash_; // Finish hash_. if (input_delay_) hashIncr(hash_, input_delay_->index()); // Finish match_hash_. hashIncr(match_hash_, clk_info_->isGenClkSrcPath()); } size_t Tag::hash(bool match_crpr_clk_pin, const StaState *sta) const { if (match_crpr_clk_pin) return hashSum(hash_, clk_info_->crprClkVertexId(sta)); else return hash_; } size_t Tag::matchHash(bool match_crpr_clk_pin, const StaState *sta) const { if (match_crpr_clk_pin) return hashSum(match_hash_, clk_info_->crprClkVertexId(sta)); else return match_hash_; } //////////////////////////////////////////////////////////////// TagLess::TagLess(const StaState *sta) : sta_(sta) { } bool TagLess::operator()(const Tag *tag1, const Tag *tag2) const { return Tag::cmp(tag1, tag2, sta_) < 0; } int Tag::cmp(const Tag *tag1, const Tag *tag2, const StaState *sta) { if (tag1 == tag2) return 0; const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); int clk_cmp = ClkInfo::cmp(clk_info1, clk_info2, sta); if (clk_cmp != 0) return clk_cmp; PathAPIndex path_ap_index1 = tag1->pathAPIndex(); PathAPIndex path_ap_index2 = tag2->pathAPIndex(); if (path_ap_index1 < path_ap_index2) return -1; if (path_ap_index1 > path_ap_index2) return 1; int rf_index1 = tag1->rfIndex(); int rf_index2 = tag2->rfIndex(); if (rf_index1 < rf_index2) return -1; if (rf_index1 > rf_index2) return 1; bool is_clk1 = tag1->isClock(); bool is_clk2 = tag2->isClock(); if (!is_clk1 && is_clk2) return -1; if (is_clk1 && !is_clk2) return 1; InputDelay *input_delay1 = tag1->inputDelay(); InputDelay *input_delay2 = tag2->inputDelay(); int input_delay_index1 = input_delay1 ? input_delay1->index() : -1; int input_delay_index2 = input_delay2 ? input_delay2->index() : -1; if (input_delay_index1 < input_delay_index2) return -1; if (input_delay_index1 > input_delay_index2) return 1; bool is_segment_start1 = tag1->isSegmentStart(); bool is_segment_start2 = tag2->isSegmentStart(); if (!is_segment_start1 && is_segment_start2) return -1; if (is_segment_start1 && !is_segment_start2) return 1; return stateCmp(tag1, tag2); } bool Tag::equal(const Tag *tag1, const Tag *tag2, const StaState *sta) { return cmp(tag1, tag2, sta) == 0; } //////////////////////////////////////////////////////////////// bool TagIndexLess::operator()(const Tag *tag1, const Tag *tag2) const { return tag1->index() < tag2->index(); } //////////////////////////////////////////////////////////////// TagMatchLess::TagMatchLess(bool match_crpr_clk_pin, const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { } bool TagMatchLess::operator()(const Tag *tag1, const Tag *tag2) const { return Tag::matchCmp(tag1, tag2, match_crpr_clk_pin_, sta_) < 0; } //////////////////////////////////////////////////////////////// bool Tag::match(const Tag *tag1, const Tag *tag2, const StaState *sta) { return Tag::matchCmp(tag1, tag2, true, sta) == 0; } bool Tag::match(const Tag *tag1, const Tag *tag2, bool match_crpr_clk_pin, const StaState *sta) { return Tag::matchCmp(tag1, tag2, match_crpr_clk_pin, sta) == 0; } int Tag::matchCmp(const Tag *tag1, const Tag *tag2, bool match_crpr_clk_pin, const StaState *sta) { if (tag1 == tag2) return 0; int rf_index1 = tag1->rfIndex(); int rf_index2 = tag2->rfIndex(); if (rf_index1 < rf_index2) return -1; if (rf_index1 > rf_index2) return 1; PathAPIndex path_ap_index1 = tag1->pathAPIndex(); PathAPIndex path_ap_index2 = tag2->pathAPIndex(); if (path_ap_index1 < path_ap_index2) return -1; if (path_ap_index1 > path_ap_index2) return 1; const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); const ClockEdge *clk_edge1 = clk_info1->clkEdge(); const ClockEdge *clk_edge2 = clk_info2->clkEdge(); int edge_index1 = clk_edge1 ? clk_edge1->index() : -1; int edge_index2 = clk_edge2 ? clk_edge2->index() : -1; if (edge_index1 < edge_index2) return -1; if (edge_index1 > edge_index2) return 1; bool is_clk1 = tag1->isClock(); bool is_clk2 = tag2->isClock(); if (!is_clk1 && is_clk2) return -1; if (is_clk1 && !is_clk2) return 1; bool is_genclk_src1 = clk_info1->isGenClkSrcPath(); bool is_genclk_src2 = clk_info2->isGenClkSrcPath(); if (!is_genclk_src1 && is_genclk_src2) return -1; if (is_genclk_src1 && !is_genclk_src2) return 1; bool is_segment_start1 = tag1->isSegmentStart(); bool is_segment_start2 = tag2->isSegmentStart(); if (!is_segment_start1 && is_segment_start2) return -1; if (is_segment_start1 && !is_segment_start2) return 1; if (match_crpr_clk_pin && sta->crprActive()) { VertexId crpr_vertex1 = clk_info1->crprClkVertexId(sta); VertexId crpr_vertex2 = clk_info2->crprClkVertexId(sta); if (crpr_vertex1 < crpr_vertex2) return -1; if (crpr_vertex1 > crpr_vertex2) return 1; } return stateCmp(tag1, tag2); } bool Tag::matchNoCrpr(const Tag *tag1, const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 || (clk_info1->clkEdge() == clk_info2->clkEdge() && tag1->rfIndex() == tag2->rfIndex() && tag1->pathAPIndex() == tag2->pathAPIndex() && tag1->isClock() == tag2->isClock() && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() && Tag::stateEqual(tag1, tag2)); } bool Tag::matchNoPathAp(const Tag *tag1, const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 || (clk_info1->clkEdge() == clk_info2->clkEdge() && tag1->rfIndex() == tag2->rfIndex() && tag1->isClock() == tag2->isClock() && tag1->isSegmentStart() == tag2->isSegmentStart() && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() && Tag::stateEqual(tag1, tag2)); } bool Tag::matchCrpr(const Tag *tag1, const Tag *tag2) { const ClkInfo *clk_info1 = tag1->clkInfo(); const ClkInfo *clk_info2 = tag2->clkInfo(); return tag1 == tag2 || (clk_info1->clkEdge() == clk_info2->clkEdge() && tag1->rfIndex() == tag2->rfIndex() && tag1->isClock() == tag2->isClock() && tag1->isSegmentStart() == tag2->isSegmentStart() && clk_info1->isGenClkSrcPath() == clk_info2->isGenClkSrcPath() && stateEqualCrpr(tag1, tag2)); } //////////////////////////////////////////////////////////////// int Tag::stateCmp(const Tag *tag1, const Tag *tag2) { ExceptionStateSet *states1 = tag1->states(); ExceptionStateSet *states2 = tag2->states(); bool states_null1 = (states1 == nullptr || states1->empty()); bool states_null2 = (states2 == nullptr || states2->empty()); if (states_null1 && states_null2) return 0; if (states_null1 && !states_null2) return -1; if (!states_null1 && states_null2) return 1; size_t state_size1 = states1->size(); size_t state_size2 = states2->size(); if (state_size1 < state_size2) return -1; if (state_size1 > state_size2) return 1; ExceptionStateSet::Iterator state_iter1(states1); ExceptionStateSet::Iterator state_iter2(states2); while (state_iter1.hasNext() && state_iter2.hasNext()) { ExceptionState *state1 = state_iter1.next(); ExceptionState *state2 = state_iter2.next(); if (exceptionStateLess(state1, state2)) return -1; if (exceptionStateLess(state2, state1)) return 1; } return 0; } bool Tag::stateEqual(const Tag *tag1, const Tag *tag2) { return stateCmp(tag1, tag2) == 0; } // Match loop exception states only for crpr min/max paths. bool Tag::stateEqualCrpr(const Tag *tag1, const Tag *tag2) { ExceptionStateSet *states1 = tag1->states(); ExceptionStateSet *states2 = tag2->states(); ExceptionStateSet::Iterator state_iter1(states1); ExceptionStateSet::Iterator state_iter2(states2); ExceptionState *state1, *state2; do { state1 = nullptr; while (state_iter1.hasNext()) { state1 = state_iter1.next(); ExceptionPath *exception1 = state1->exception(); if (exception1->isLoop()) break; else state1 = nullptr; } state2 = nullptr; while (state_iter2.hasNext()) { state2 = state_iter2.next(); ExceptionPath *exception2 = state2->exception(); if (exception2->isLoop()) break; else state2 = nullptr; } if (state1 != state2) return false; } while (state1 && state2); return state1 == nullptr && state2 == nullptr; } //////////////////////////////////////////////////////////////// TagHash::TagHash(const StaState *sta) : sta_(sta) { } size_t TagHash::operator()(const Tag *tag) const { bool crpr_on = sta_->crprActive(); return tag->matchHash(crpr_on, sta_); } TagEqual::TagEqual(const StaState *sta) : sta_(sta) { } bool TagEqual::operator()(const Tag *tag1, const Tag *tag2) const { return Tag::equal(tag1, tag2, sta_); } TagMatchHash::TagMatchHash(bool match_crpr_clk_pin, const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { } size_t TagMatchHash::operator()(const Tag *tag) const { return tag->matchHash(match_crpr_clk_pin_, sta_); } TagMatchEqual::TagMatchEqual(bool match_crpr_clk_pin, const StaState *sta) : match_crpr_clk_pin_(match_crpr_clk_pin), sta_(sta) { } bool TagMatchEqual::operator()(const Tag *tag1, const Tag *tag2) const { return Tag::match(tag1, tag2, match_crpr_clk_pin_, sta_); } } // namespace