2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2025-01-22 02:54:33 +01:00
|
|
|
// Copyright (c) 2025, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// 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
|
2022-01-04 18:17:08 +01:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2018-09-28 17:54:21 +02:00
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
2022-01-04 18:17:08 +01:00
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2025-01-22 02:54:33 +01:00
|
|
|
//
|
|
|
|
|
// 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.
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "PathGroup.hh"
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <limits>
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "Stats.hh"
|
|
|
|
|
#include "Debug.hh"
|
|
|
|
|
#include "Mutex.hh"
|
|
|
|
|
#include "Fuzzy.hh"
|
|
|
|
|
#include "MinMax.hh"
|
|
|
|
|
#include "DispatchQueue.hh"
|
|
|
|
|
#include "ExceptionPath.hh"
|
|
|
|
|
#include "Sdc.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "PathEnd.hh"
|
|
|
|
|
#include "PathAnalysisPt.hh"
|
|
|
|
|
#include "Tag.hh"
|
|
|
|
|
#include "Corner.hh"
|
|
|
|
|
#include "Search.hh"
|
|
|
|
|
#include "VisitPathEnds.hh"
|
|
|
|
|
#include "PathEnum.hh"
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
2025-03-27 02:21:03 +01:00
|
|
|
size_t PathGroup::group_path_count_max = std::numeric_limits<size_t>::max();
|
2019-02-16 21:07:59 +01:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
PathGroup *
|
|
|
|
|
PathGroup::makePathGroupSlack(const char *name,
|
2024-11-24 00:38:26 +01:00
|
|
|
int group_path_count,
|
|
|
|
|
int endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
float slack_min,
|
|
|
|
|
float slack_max,
|
|
|
|
|
const StaState *sta)
|
|
|
|
|
{
|
2024-11-24 00:38:26 +01:00
|
|
|
return new PathGroup(name, group_path_count, endpoint_path_count, unique_pins,
|
2018-09-28 17:54:21 +02:00
|
|
|
slack_min, slack_max, true, MinMax::min(), sta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroup *
|
|
|
|
|
PathGroup::makePathGroupArrival(const char *name,
|
2024-11-24 00:38:26 +01:00
|
|
|
int group_path_count,
|
|
|
|
|
int endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const StaState *sta)
|
|
|
|
|
{
|
2024-11-24 00:38:26 +01:00
|
|
|
return new PathGroup(name, group_path_count, endpoint_path_count, unique_pins,
|
2018-09-28 17:54:21 +02:00
|
|
|
0.0, 0.0, false, min_max, sta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroup::PathGroup(const char *name,
|
2025-03-27 02:21:03 +01:00
|
|
|
size_t group_path_count,
|
|
|
|
|
size_t endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
float slack_min,
|
|
|
|
|
float slack_max,
|
|
|
|
|
bool cmp_slack,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const StaState *sta) :
|
|
|
|
|
name_(name),
|
2024-11-24 00:38:26 +01:00
|
|
|
group_path_count_(group_path_count),
|
|
|
|
|
endpoint_path_count_(endpoint_path_count),
|
2018-09-28 17:54:21 +02:00
|
|
|
unique_pins_(unique_pins),
|
|
|
|
|
slack_min_(slack_min),
|
|
|
|
|
slack_max_(slack_max),
|
|
|
|
|
min_max_(min_max),
|
|
|
|
|
compare_slack_(cmp_slack),
|
|
|
|
|
threshold_(min_max->initValue()),
|
|
|
|
|
sta_(sta)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroup::~PathGroup()
|
|
|
|
|
{
|
|
|
|
|
path_ends_.deleteContents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2025-01-14 00:58:47 +01:00
|
|
|
PathGroup::saveable(PathEnd *path_end)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2024-11-08 02:18:27 +01:00
|
|
|
float threshold;
|
|
|
|
|
{
|
|
|
|
|
LockGuard lock(lock_);
|
|
|
|
|
threshold = threshold_;
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
if (compare_slack_) {
|
|
|
|
|
// Crpr increases the slack, so check the slack
|
|
|
|
|
// without crpr first because it is expensive to find.
|
|
|
|
|
Slack slack = path_end->slackNoCrpr(sta_);
|
|
|
|
|
if (!delayIsInitValue(slack, min_max_)
|
2024-11-08 02:18:27 +01:00
|
|
|
&& delayLessEqual(slack, threshold, sta_)
|
2020-07-12 02:43:30 +02:00
|
|
|
&& delayLessEqual(slack, slack_max_, sta_)) {
|
2018-09-28 17:54:21 +02:00
|
|
|
// Now check with crpr.
|
|
|
|
|
slack = path_end->slack(sta_);
|
2025-01-14 00:58:47 +01:00
|
|
|
return delayLessEqual(slack, threshold, sta_)
|
2020-07-12 02:43:30 +02:00
|
|
|
&& delayLessEqual(slack, slack_max_, sta_)
|
|
|
|
|
&& delayGreaterEqual(slack, slack_min_, sta_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const Arrival &arrival = path_end->dataArrivalTime(sta_);
|
2025-01-14 00:58:47 +01:00
|
|
|
return !delayIsInitValue(arrival, min_max_)
|
2024-11-08 02:18:27 +01:00
|
|
|
&& delayGreaterEqual(arrival, threshold, min_max_, sta_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2025-01-14 00:58:47 +01:00
|
|
|
return false;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-15 17:17:14 +01:00
|
|
|
// endpoint_path_count > 1 with slack_min requires
|
|
|
|
|
// saving endpoints with slack > slack_min so that
|
2025-02-24 00:21:44 +01:00
|
|
|
// path enumeration can find them. Use the path end
|
2025-01-15 17:17:14 +01:00
|
|
|
// with the min(max) delay to prune ends that cannot
|
|
|
|
|
// onion peel down to slack_min.
|
|
|
|
|
bool
|
|
|
|
|
PathGroup::enumMinSlackUnderMin(PathEnd *path_end)
|
|
|
|
|
{
|
|
|
|
|
if (compare_slack_
|
|
|
|
|
&& endpoint_path_count_ > 1
|
|
|
|
|
&& slack_min_ > -INF) {
|
|
|
|
|
const Path *path = path_end->path();
|
|
|
|
|
PathAnalysisPt *other_ap = path->pathAnalysisPt(sta_)->tgtClkAnalysisPt();
|
|
|
|
|
const Tag *tag = path->tag(sta_);
|
|
|
|
|
VertexPathIterator other_iter(path->vertex(sta_),
|
|
|
|
|
path->transition(sta_),
|
|
|
|
|
other_ap, sta_);
|
|
|
|
|
while (other_iter.hasNext()) {
|
2025-03-27 02:21:03 +01:00
|
|
|
Path *other = other_iter.next();
|
2025-09-16 23:49:01 +02:00
|
|
|
if (Tag::matchCrpr(other->tag(sta_), tag)) {
|
2025-01-15 17:17:14 +01:00
|
|
|
PathEnd *end_min = path_end->copy();
|
|
|
|
|
end_min->setPath(other);
|
2025-04-10 01:35:15 +02:00
|
|
|
float slack = delayAsFloat(end_min->slackNoCrpr(sta_));
|
|
|
|
|
bool slack_under = fuzzyGreater(slack, slack_min_);
|
2025-01-15 17:17:14 +01:00
|
|
|
delete end_min;
|
|
|
|
|
if (slack_under)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
void
|
|
|
|
|
PathGroup::insert(PathEnd *path_end)
|
|
|
|
|
{
|
2024-06-03 23:09:30 +02:00
|
|
|
LockGuard lock(lock_);
|
2018-09-28 17:54:21 +02:00
|
|
|
path_ends_.push_back(path_end);
|
2024-11-24 00:38:26 +01:00
|
|
|
if (group_path_count_ != group_path_count_max
|
2025-03-27 02:21:03 +01:00
|
|
|
&& path_ends_.size() > group_path_count_ * 2)
|
2018-09-28 17:54:21 +02:00
|
|
|
prune();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathGroup::prune()
|
|
|
|
|
{
|
|
|
|
|
sort();
|
|
|
|
|
VertexPathCountMap path_counts;
|
2025-03-27 02:21:03 +01:00
|
|
|
size_t end_count = 0;
|
2018-09-28 17:54:21 +02:00
|
|
|
for (unsigned i = 0; i < path_ends_.size(); i++) {
|
|
|
|
|
PathEnd *path_end = path_ends_[i];
|
|
|
|
|
Vertex *vertex = path_end->vertex(sta_);
|
2024-11-24 00:38:26 +01:00
|
|
|
// Squish up to endpoint_path_count path ends per vertex
|
|
|
|
|
// up to the front of path_ends_.
|
|
|
|
|
if (end_count < group_path_count_
|
|
|
|
|
&& path_counts[vertex] < endpoint_path_count_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
path_ends_[end_count++] = path_end;
|
|
|
|
|
path_counts[vertex]++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
delete path_end;
|
|
|
|
|
}
|
|
|
|
|
path_ends_.resize(end_count);
|
|
|
|
|
|
|
|
|
|
// Set a threshold to the bottom of the sorted list that future
|
|
|
|
|
// inserts need to beat.
|
|
|
|
|
PathEnd *last_end = path_ends_[end_count - 1];
|
|
|
|
|
if (compare_slack_)
|
|
|
|
|
threshold_ = delayAsFloat(last_end->slack(sta_));
|
|
|
|
|
else
|
|
|
|
|
threshold_ = delayAsFloat(last_end->dataArrivalTime(sta_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-20 19:19:39 +01:00
|
|
|
PathGroup::pushEnds(PathEndSeq &path_ends)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
ensureSortedMaxPaths();
|
2020-01-07 04:36:04 +01:00
|
|
|
for (PathEnd *path_end : path_ends_)
|
2023-01-20 19:19:39 +01:00
|
|
|
path_ends.push_back(path_end);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroupIterator *
|
|
|
|
|
PathGroup::iterator()
|
|
|
|
|
{
|
|
|
|
|
ensureSortedMaxPaths();
|
|
|
|
|
return new PathGroupIterator(path_ends_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathGroup::ensureSortedMaxPaths()
|
|
|
|
|
{
|
2025-03-27 02:21:03 +01:00
|
|
|
if (path_ends_.size() > group_path_count_)
|
2018-09-28 17:54:21 +02:00
|
|
|
prune();
|
|
|
|
|
else
|
|
|
|
|
sort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathGroup::sort()
|
|
|
|
|
{
|
|
|
|
|
sta::sort(path_ends_, PathEndLess(sta_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathGroup::clear()
|
|
|
|
|
{
|
2024-06-03 23:09:30 +02:00
|
|
|
LockGuard lock(lock_);
|
2018-09-28 17:54:21 +02:00
|
|
|
threshold_ = min_max_->initValue();
|
|
|
|
|
path_ends_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-01-20 23:19:07 +01:00
|
|
|
const char *PathGroups::path_delay_group_name_ = "path delay";
|
|
|
|
|
const char *PathGroups::gated_clk_group_name_ = "gated clock";
|
|
|
|
|
const char *PathGroups::async_group_name_ = "asynchronous";
|
|
|
|
|
const char *PathGroups::unconstrained_group_name_ = "unconstrained";
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2025-10-25 19:02:34 +02:00
|
|
|
PathGroups::PathGroups(const StaState *sta) :
|
|
|
|
|
StaState(sta),
|
|
|
|
|
group_path_count_(0),
|
|
|
|
|
endpoint_path_count_(0),
|
|
|
|
|
unique_pins_(false),
|
|
|
|
|
slack_min_(-INF),
|
|
|
|
|
slack_max_(INF)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2025-10-25 20:27:01 +02:00
|
|
|
makeGroups(group_path_count_, endpoint_path_count_, unique_pins_,
|
|
|
|
|
slack_min_, slack_max_, nullptr,
|
|
|
|
|
true, true, true, true, MinMax::max());
|
|
|
|
|
makeGroups(group_path_count_, endpoint_path_count_, unique_pins_,
|
|
|
|
|
slack_min_, slack_max_, nullptr,
|
|
|
|
|
true, true, true, true, MinMax::min());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-24 00:38:26 +01:00
|
|
|
PathGroups::PathGroups(int group_path_count,
|
|
|
|
|
int endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
float slack_min,
|
|
|
|
|
float slack_max,
|
|
|
|
|
PathGroupNameSet *group_names,
|
|
|
|
|
bool setup,
|
|
|
|
|
bool hold,
|
|
|
|
|
bool recovery,
|
|
|
|
|
bool removal,
|
|
|
|
|
bool clk_gating_setup,
|
|
|
|
|
bool clk_gating_hold,
|
|
|
|
|
bool unconstrained,
|
|
|
|
|
const StaState *sta) :
|
|
|
|
|
StaState(sta),
|
2024-11-24 00:38:26 +01:00
|
|
|
group_path_count_(group_path_count),
|
|
|
|
|
endpoint_path_count_(endpoint_path_count),
|
2018-09-28 17:54:21 +02:00
|
|
|
unique_pins_(unique_pins),
|
|
|
|
|
slack_min_(slack_min),
|
|
|
|
|
slack_max_(slack_max)
|
|
|
|
|
{
|
2024-11-24 00:38:26 +01:00
|
|
|
makeGroups(group_path_count, endpoint_path_count, unique_pins,
|
|
|
|
|
slack_min, slack_max, group_names,
|
2018-09-28 17:54:21 +02:00
|
|
|
setup, recovery, clk_gating_setup, unconstrained,
|
|
|
|
|
MinMax::max());
|
2024-11-24 00:38:26 +01:00
|
|
|
makeGroups(group_path_count, endpoint_path_count, unique_pins,
|
|
|
|
|
slack_min, slack_max, group_names,
|
2018-09-28 17:54:21 +02:00
|
|
|
hold, removal, clk_gating_hold, unconstrained,
|
|
|
|
|
MinMax::min());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2024-11-24 00:38:26 +01:00
|
|
|
PathGroups::makeGroups(int group_path_count,
|
|
|
|
|
int endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
float slack_min,
|
|
|
|
|
float slack_max,
|
|
|
|
|
PathGroupNameSet *group_names,
|
|
|
|
|
bool setup_hold,
|
|
|
|
|
bool async,
|
|
|
|
|
bool gated_clk,
|
|
|
|
|
bool unconstrained,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
int mm_index = min_max->index();
|
|
|
|
|
if (setup_hold) {
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [name, group] : sdc_->groupPaths()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (reportGroup(name, group_names)) {
|
2024-11-24 00:38:26 +01:00
|
|
|
PathGroup *group = PathGroup::makePathGroupSlack(name,
|
|
|
|
|
group_path_count,
|
|
|
|
|
endpoint_path_count,
|
|
|
|
|
unique_pins,
|
2018-09-28 17:54:21 +02:00
|
|
|
slack_min, slack_max,
|
|
|
|
|
this);
|
|
|
|
|
named_map_[mm_index][name] = group;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk : sdc_->clks()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
const char *clk_name = clk->name();
|
|
|
|
|
if (reportGroup(clk_name, group_names)) {
|
2024-11-24 00:38:26 +01:00
|
|
|
PathGroup *group = PathGroup::makePathGroupSlack(clk_name,
|
|
|
|
|
group_path_count,
|
|
|
|
|
endpoint_path_count,
|
|
|
|
|
unique_pins,
|
2018-09-28 17:54:21 +02:00
|
|
|
slack_min, slack_max,
|
|
|
|
|
this);
|
|
|
|
|
clk_map_[mm_index][clk] = group;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (setup_hold
|
|
|
|
|
&& reportGroup(path_delay_group_name_, group_names))
|
|
|
|
|
path_delay_[mm_index] = PathGroup::makePathGroupSlack(path_delay_group_name_,
|
2024-11-24 00:38:26 +01:00
|
|
|
group_path_count,
|
|
|
|
|
endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
unique_pins,
|
|
|
|
|
slack_min, slack_max,
|
|
|
|
|
this);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
path_delay_[mm_index] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
if (gated_clk
|
|
|
|
|
&& reportGroup(gated_clk_group_name_, group_names))
|
|
|
|
|
gated_clk_[mm_index] = PathGroup::makePathGroupSlack(gated_clk_group_name_,
|
2024-11-24 00:38:26 +01:00
|
|
|
group_path_count,
|
|
|
|
|
endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
unique_pins,
|
|
|
|
|
slack_min, slack_max,
|
|
|
|
|
this);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
gated_clk_[mm_index] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
if (async
|
|
|
|
|
&& reportGroup(async_group_name_, group_names))
|
|
|
|
|
async_[mm_index] = PathGroup::makePathGroupSlack(async_group_name_,
|
2024-11-24 00:38:26 +01:00
|
|
|
group_path_count,
|
|
|
|
|
endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
unique_pins,
|
|
|
|
|
slack_min, slack_max,
|
|
|
|
|
this);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
async_[mm_index] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
if (unconstrained
|
|
|
|
|
&& reportGroup(unconstrained_group_name_, group_names))
|
|
|
|
|
unconstrained_[mm_index] =
|
|
|
|
|
PathGroup::makePathGroupArrival(unconstrained_group_name_,
|
2024-11-24 00:38:26 +01:00
|
|
|
group_path_count, endpoint_path_count,
|
|
|
|
|
unique_pins, min_max, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
unconstrained_[mm_index] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroups::~PathGroups()
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto mm_index : MinMax::rangeIndex()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
named_map_[mm_index].deleteContents();
|
|
|
|
|
clk_map_[mm_index].deleteContents();
|
|
|
|
|
delete path_delay_[mm_index];
|
|
|
|
|
delete gated_clk_[mm_index];
|
|
|
|
|
delete async_[mm_index];
|
|
|
|
|
delete unconstrained_[mm_index];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroup *
|
|
|
|
|
PathGroups::findPathGroup(const char *name,
|
|
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
|
|
|
|
return named_map_[min_max->index()].findKey(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroup *
|
|
|
|
|
PathGroups::findPathGroup(const Clock *clock,
|
|
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
|
|
|
|
return clk_map_[min_max->index()].findKey(clock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
PathGroups::reportGroup(const char *group_name,
|
|
|
|
|
PathGroupNameSet *group_names) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return group_names == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
|| group_names->empty()
|
|
|
|
|
|| group_names->hasKey(group_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroup *
|
|
|
|
|
PathGroups::pathGroup(const PathEnd *path_end) const
|
|
|
|
|
{
|
|
|
|
|
const MinMax *min_max = path_end->minMax(this);
|
|
|
|
|
int mm_index = min_max->index();
|
|
|
|
|
GroupPath *group_path = groupPathTo(path_end);
|
2025-03-22 00:39:32 +01:00
|
|
|
if (path_end->isUnconstrained())
|
|
|
|
|
return unconstrained_[mm_index];
|
|
|
|
|
// GroupPaths have precedence.
|
|
|
|
|
else if (group_path) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (group_path->isDefault())
|
|
|
|
|
return path_delay_[mm_index];
|
|
|
|
|
else {
|
|
|
|
|
const char *group_name = group_path->name();
|
|
|
|
|
return findPathGroup(group_name, min_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (path_end->isCheck() || path_end->isLatchCheck()) {
|
|
|
|
|
const TimingRole *check_role = path_end->checkRole(this);
|
|
|
|
|
const Clock *tgt_clk = path_end->targetClk(this);
|
|
|
|
|
if (check_role == TimingRole::removal()
|
|
|
|
|
|| check_role == TimingRole::recovery())
|
|
|
|
|
return async_[mm_index];
|
|
|
|
|
else
|
|
|
|
|
return findPathGroup(tgt_clk, min_max);
|
|
|
|
|
}
|
|
|
|
|
else if (path_end->isOutputDelay()
|
|
|
|
|
|| path_end->isDataCheck())
|
|
|
|
|
return findPathGroup(path_end->targetClk(this), min_max);
|
|
|
|
|
else if (path_end->isGatedClock())
|
|
|
|
|
return gated_clk_[mm_index];
|
|
|
|
|
else if (path_end->isPathDelay()) {
|
|
|
|
|
// Path delays that end at timing checks are part of the target clk group
|
|
|
|
|
// unless -ignore_clock_latency is true.
|
|
|
|
|
PathDelay *path_delay = path_end->pathDelay();
|
2023-01-19 19:23:45 +01:00
|
|
|
const Clock *tgt_clk = path_end->targetClk(this);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (tgt_clk
|
|
|
|
|
&& !path_delay->ignoreClkLatency())
|
|
|
|
|
return findPathGroup(tgt_clk, min_max);
|
|
|
|
|
else
|
|
|
|
|
return path_delay_[mm_index];
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-01-08 03:23:53 +01:00
|
|
|
report_->critical(1390, "unknown path end type");
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupPath *
|
|
|
|
|
PathGroups::groupPathTo(const PathEnd *path_end) const
|
|
|
|
|
{
|
|
|
|
|
const Path *path = path_end->path();
|
|
|
|
|
const Pin *pin = path->pin(this);
|
|
|
|
|
ExceptionPath *exception =
|
2019-03-13 01:25:53 +01:00
|
|
|
search_->exceptionTo(ExceptionPathType::group_path, path,
|
2018-09-28 17:54:21 +02:00
|
|
|
pin, path->transition(this),
|
|
|
|
|
path_end->targetClkEdge(this),
|
|
|
|
|
path->minMax(this), false, false);
|
|
|
|
|
return dynamic_cast<GroupPath*>(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-20 19:19:39 +01:00
|
|
|
PathGroups::pushGroupPathEnds(PathEndSeq &path_ends)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto min_max : MinMax::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
int mm_index = min_max->index();
|
2023-01-19 19:23:45 +01:00
|
|
|
for (auto name_group : sdc_->groupPaths()) {
|
|
|
|
|
const char *name = name_group.first;
|
2018-09-28 17:54:21 +02:00
|
|
|
PathGroup *path_group = findPathGroup(name, min_max);
|
|
|
|
|
if (path_group)
|
|
|
|
|
path_group->pushEnds(path_ends);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (async_[mm_index])
|
|
|
|
|
async_[mm_index]->pushEnds(path_ends);
|
|
|
|
|
|
|
|
|
|
if (gated_clk_[mm_index])
|
|
|
|
|
gated_clk_[mm_index]->pushEnds(path_ends);
|
|
|
|
|
|
|
|
|
|
if (path_delay_[mm_index])
|
|
|
|
|
path_delay_[mm_index]->pushEnds(path_ends);
|
|
|
|
|
|
|
|
|
|
ClockSeq clks;
|
|
|
|
|
sdc_->sortedClocks(clks);
|
|
|
|
|
ClockSeq::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
PathGroup *path_group = findPathGroup(clk, min_max);
|
|
|
|
|
if (path_group)
|
|
|
|
|
path_group->pushEnds(path_ends);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-20 19:19:39 +01:00
|
|
|
PathGroups::pushUnconstrainedPathEnds(PathEndSeq &path_ends,
|
2018-09-28 17:54:21 +02:00
|
|
|
const MinMaxAll *min_max)
|
|
|
|
|
{
|
|
|
|
|
Set<PathGroup *> groups;
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto path_ap : corners_->pathAnalysisPts()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
const MinMax *path_min_max = path_ap->pathMinMax();
|
|
|
|
|
if (min_max->matches(path_min_max)) {
|
|
|
|
|
int mm_index = path_min_max->index();
|
|
|
|
|
PathGroup *group = unconstrained_[mm_index];
|
|
|
|
|
if (group
|
|
|
|
|
// For multiple corner path APs use the same group.
|
|
|
|
|
// Only report it once.
|
|
|
|
|
&& !groups.findKey(group)) {
|
|
|
|
|
group->pushEnds(path_ends);
|
|
|
|
|
groups.insert(group);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
typedef Map<PathGroup*, PathEnd*> PathGroupEndMap;
|
|
|
|
|
typedef Map<PathGroup*, PathEndSeq*> PathGroupEndsMap;
|
|
|
|
|
typedef Set<PathEnd*, PathEndNoCrprLess> PathEndNoCrprSet;
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
exceptionToEmpty(ExceptionTo *to);
|
|
|
|
|
|
2023-01-20 19:19:39 +01:00
|
|
|
PathEndSeq
|
2018-09-28 17:54:21 +02:00
|
|
|
PathGroups::makePathEnds(ExceptionTo *to,
|
2019-01-06 01:09:27 +01:00
|
|
|
bool unconstrained_paths,
|
2018-09-28 17:54:21 +02:00
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
bool sort_by_slack)
|
|
|
|
|
{
|
2020-12-29 19:33:22 +01:00
|
|
|
Stats stats(debug_, report_);
|
2024-11-24 00:38:26 +01:00
|
|
|
makeGroupPathEnds(to, group_path_count_, endpoint_path_count_, unique_pins_,
|
2019-01-06 01:09:27 +01:00
|
|
|
corner, min_max);
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2023-01-20 19:19:39 +01:00
|
|
|
PathEndSeq path_ends;
|
2018-09-28 17:54:21 +02:00
|
|
|
pushGroupPathEnds(path_ends);
|
|
|
|
|
if (sort_by_slack) {
|
|
|
|
|
sort(path_ends, PathEndLess(this));
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-06 01:09:27 +01:00
|
|
|
if (unconstrained_paths
|
2023-01-20 19:19:39 +01:00
|
|
|
&& path_ends.empty())
|
2018-09-28 17:54:21 +02:00
|
|
|
// No constrained paths, so report unconstrained paths.
|
|
|
|
|
pushUnconstrainedPathEnds(path_ends, min_max);
|
|
|
|
|
|
|
|
|
|
stats.report("Make path ends");
|
|
|
|
|
return path_ends;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Visit each path end for a vertex and add the worst one in each
|
|
|
|
|
// path group to the group.
|
|
|
|
|
class MakePathEnds1 : public PathEndVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2021-12-16 01:21:57 +01:00
|
|
|
MakePathEnds1(PathGroups *path_groups);
|
|
|
|
|
MakePathEnds1(const MakePathEnds1&) = default;
|
|
|
|
|
virtual PathEndVisitor *copy() const;
|
2018-09-28 17:54:21 +02:00
|
|
|
virtual void visit(PathEnd *path_end);
|
|
|
|
|
virtual void vertexEnd(Vertex *vertex);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void visitPathEnd(PathEnd *path_end,
|
|
|
|
|
PathGroup *group);
|
|
|
|
|
|
|
|
|
|
PathGroups *path_groups_;
|
|
|
|
|
PathGroupEndMap ends_;
|
|
|
|
|
PathEndLess cmp_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
MakePathEnds1::MakePathEnds1(PathGroups *path_groups) :
|
|
|
|
|
path_groups_(path_groups),
|
2021-12-16 01:21:57 +01:00
|
|
|
cmp_(path_groups)
|
|
|
|
|
{
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathEndVisitor *
|
2021-12-16 01:21:57 +01:00
|
|
|
MakePathEnds1::copy() const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2021-12-16 01:21:57 +01:00
|
|
|
return new MakePathEnds1(*this);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
MakePathEnds1::visit(PathEnd *path_end)
|
|
|
|
|
{
|
|
|
|
|
PathGroup *group = path_groups_->pathGroup(path_end);
|
|
|
|
|
if (group)
|
|
|
|
|
visitPathEnd(path_end, group);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
MakePathEnds1::visitPathEnd(PathEnd *path_end,
|
|
|
|
|
PathGroup *group)
|
|
|
|
|
{
|
2025-01-14 00:58:47 +01:00
|
|
|
if (group->saveable(path_end)) {
|
2018-09-28 17:54:21 +02:00
|
|
|
// Only keep the path end with the smallest slack/latest arrival.
|
|
|
|
|
PathEnd *worst_end = ends_.findKey(group);
|
|
|
|
|
if (worst_end) {
|
|
|
|
|
if (cmp_(path_end, worst_end)) {
|
|
|
|
|
ends_[group] = path_end->copy();
|
|
|
|
|
delete worst_end;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ends_[group] = path_end->copy();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save the worst end for each path group.
|
|
|
|
|
void
|
|
|
|
|
MakePathEnds1::vertexEnd(Vertex *)
|
|
|
|
|
{
|
|
|
|
|
PathGroupEndMap::Iterator group_iter(ends_);
|
|
|
|
|
while (group_iter.hasNext()) {
|
|
|
|
|
PathGroup *group;
|
|
|
|
|
PathEnd *end;
|
|
|
|
|
group_iter.next(group, end);
|
2025-01-14 00:58:47 +01:00
|
|
|
// visitPathEnd already confirmed slack is saveable.
|
2018-09-28 17:54:21 +02:00
|
|
|
if (end) {
|
|
|
|
|
group->insert(end);
|
|
|
|
|
// Clear ends_ for next vertex.
|
2019-03-13 01:25:53 +01:00
|
|
|
ends_[group] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Visit each path end and add it to the corresponding path group.
|
|
|
|
|
// After collecting the ends do parallel path enumeration to find the
|
|
|
|
|
// path ends for the group.
|
|
|
|
|
class MakePathEndsAll : public PathEndVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2024-11-24 00:38:26 +01:00
|
|
|
MakePathEndsAll(int endpoint_path_count,
|
2021-12-16 01:21:57 +01:00
|
|
|
PathGroups *path_groups);
|
|
|
|
|
MakePathEndsAll(const MakePathEndsAll&) = default;
|
2018-09-28 17:54:21 +02:00
|
|
|
virtual ~MakePathEndsAll();
|
2021-12-16 01:21:57 +01:00
|
|
|
virtual PathEndVisitor *copy() const;
|
2018-09-28 17:54:21 +02:00
|
|
|
virtual void visit(PathEnd *path_end);
|
|
|
|
|
virtual void vertexEnd(Vertex *vertex);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void visitPathEnd(PathEnd *path_end,
|
|
|
|
|
PathGroup *group);
|
|
|
|
|
|
2024-11-24 00:38:26 +01:00
|
|
|
int endpoint_path_count_;
|
2018-09-28 17:54:21 +02:00
|
|
|
PathGroups *path_groups_;
|
|
|
|
|
const StaState *sta_;
|
|
|
|
|
PathGroupEndsMap ends_;
|
|
|
|
|
PathEndSlackLess slack_cmp_;
|
|
|
|
|
PathEndNoCrprLess path_no_crpr_cmp_;
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-24 00:38:26 +01:00
|
|
|
MakePathEndsAll::MakePathEndsAll(int endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
PathGroups *path_groups) :
|
2024-11-24 00:38:26 +01:00
|
|
|
endpoint_path_count_(endpoint_path_count),
|
2018-09-28 17:54:21 +02:00
|
|
|
path_groups_(path_groups),
|
|
|
|
|
sta_(path_groups),
|
|
|
|
|
slack_cmp_(path_groups),
|
|
|
|
|
path_no_crpr_cmp_(path_groups)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PathEndVisitor *
|
2021-12-16 01:21:57 +01:00
|
|
|
MakePathEndsAll::copy() const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2021-12-16 01:21:57 +01:00
|
|
|
return new MakePathEndsAll(*this);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MakePathEndsAll::~MakePathEndsAll()
|
|
|
|
|
{
|
|
|
|
|
PathGroupEndsMap::Iterator group_iter(ends_);
|
|
|
|
|
while (group_iter.hasNext()) {
|
|
|
|
|
PathGroup *group;
|
|
|
|
|
PathEndSeq *ends;
|
|
|
|
|
group_iter.next(group, ends);
|
|
|
|
|
delete ends;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
MakePathEndsAll::visit(PathEnd *path_end)
|
|
|
|
|
{
|
|
|
|
|
PathGroup *group = path_groups_->pathGroup(path_end);
|
|
|
|
|
if (group)
|
|
|
|
|
visitPathEnd(path_end, group);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
MakePathEndsAll::visitPathEnd(PathEnd *path_end,
|
|
|
|
|
PathGroup *group)
|
|
|
|
|
{
|
|
|
|
|
PathEndSeq *ends = ends_.findKey(group);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (ends == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
ends = new PathEndSeq;
|
|
|
|
|
ends_[group] = ends;
|
|
|
|
|
}
|
|
|
|
|
ends->push_back(path_end->copy());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
MakePathEndsAll::vertexEnd(Vertex *)
|
|
|
|
|
{
|
|
|
|
|
Debug *debug = sta_->debug();
|
|
|
|
|
PathGroupEndsMap::Iterator group_iter(ends_);
|
|
|
|
|
while (group_iter.hasNext()) {
|
|
|
|
|
PathGroup *group;
|
|
|
|
|
PathEndSeq *ends;
|
|
|
|
|
group_iter.next(group, ends);
|
|
|
|
|
if (ends) {
|
|
|
|
|
sort(ends, slack_cmp_);
|
|
|
|
|
PathEndNoCrprSet unique_ends(path_no_crpr_cmp_);
|
|
|
|
|
PathEndSeq::Iterator end_iter(ends);
|
|
|
|
|
int n = 0;
|
|
|
|
|
while (end_iter.hasNext()
|
2024-11-24 00:38:26 +01:00
|
|
|
&& n < endpoint_path_count_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
PathEnd *path_end = end_iter.next();
|
|
|
|
|
// Only save the worst path end for each crpr tag.
|
|
|
|
|
// PathEnum will peel the others.
|
|
|
|
|
if (!unique_ends.hasKey(path_end)) {
|
2025-04-23 20:38:44 +02:00
|
|
|
debugPrint(debug, "path_group", 2, "insert %s %s %s %d",
|
2025-03-31 00:27:53 +02:00
|
|
|
path_end->vertex(sta_)->to_string(sta_).c_str(),
|
2021-01-01 20:46:51 +01:00
|
|
|
path_end->typeName(),
|
2025-03-31 00:27:53 +02:00
|
|
|
path_end->transition(sta_)->to_string().c_str(),
|
2021-01-01 20:46:51 +01:00
|
|
|
path_end->path()->tag(sta_)->index());
|
2018-09-28 17:54:21 +02:00
|
|
|
// Give the group a copy of the path end because
|
|
|
|
|
// it may delete it during pruning.
|
2025-01-15 17:17:14 +01:00
|
|
|
if (group->saveable(path_end)
|
|
|
|
|
|| group->enumMinSlackUnderMin(path_end)) {
|
2018-09-28 17:54:21 +02:00
|
|
|
group->insert(path_end->copy());
|
|
|
|
|
unique_ends.insert(path_end);
|
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2025-04-23 20:38:44 +02:00
|
|
|
debugPrint(debug, "path_group", 3, "prune %s %s %s %d",
|
2025-03-31 00:27:53 +02:00
|
|
|
path_end->vertex(sta_)->to_string(sta_).c_str(),
|
2021-01-01 20:46:51 +01:00
|
|
|
path_end->typeName(),
|
2025-03-31 00:27:53 +02:00
|
|
|
path_end->transition(sta_)->to_string().c_str(),
|
2021-01-01 20:46:51 +01:00
|
|
|
path_end->path()->tag(sta_)->index());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
// Clear ends for next vertex.
|
|
|
|
|
PathEndSeq::Iterator end_iter2(ends);
|
|
|
|
|
while (end_iter2.hasNext()) {
|
|
|
|
|
PathEnd *path_end = end_iter2.next();
|
|
|
|
|
delete path_end;
|
|
|
|
|
}
|
|
|
|
|
ends->clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathGroups::makeGroupPathEnds(ExceptionTo *to,
|
2024-11-24 00:38:26 +01:00
|
|
|
int group_path_count,
|
|
|
|
|
int endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMaxAll *min_max)
|
|
|
|
|
{
|
2024-11-24 00:38:26 +01:00
|
|
|
if (endpoint_path_count == 1) {
|
2018-09-28 17:54:21 +02:00
|
|
|
MakePathEnds1 make_path_ends(this);
|
|
|
|
|
makeGroupPathEnds(to, corner, min_max, &make_path_ends);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-11-24 00:38:26 +01:00
|
|
|
MakePathEndsAll make_path_ends(endpoint_path_count, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
makeGroupPathEnds(to, corner, min_max, &make_path_ends);
|
|
|
|
|
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto path_min_max : MinMax::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
int mm_index = path_min_max->index();
|
2023-01-19 19:23:45 +01:00
|
|
|
for (auto name_group : sdc_->groupPaths()) {
|
|
|
|
|
const char *name = name_group.first;
|
|
|
|
|
PathGroup *group = findPathGroup(name, path_min_max);
|
|
|
|
|
if (group)
|
2024-11-24 00:38:26 +01:00
|
|
|
enumPathEnds(group, group_path_count, endpoint_path_count, unique_pins, true);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk : sdc_->clks()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
PathGroup *group = findPathGroup(clk, path_min_max);
|
|
|
|
|
if (group)
|
2024-11-24 00:38:26 +01:00
|
|
|
enumPathEnds(group, group_path_count, endpoint_path_count, unique_pins, true);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathGroup *group = unconstrained_[mm_index];
|
|
|
|
|
if (group)
|
2024-11-24 00:38:26 +01:00
|
|
|
enumPathEnds(group, group_path_count, endpoint_path_count, unique_pins, false);
|
2018-09-28 17:54:21 +02:00
|
|
|
group = path_delay_[mm_index];
|
|
|
|
|
if (group)
|
2024-11-24 00:38:26 +01:00
|
|
|
enumPathEnds(group, group_path_count, endpoint_path_count, unique_pins, true);
|
2018-09-28 17:54:21 +02:00
|
|
|
group = gated_clk_[mm_index];
|
|
|
|
|
if (group)
|
2024-11-24 00:38:26 +01:00
|
|
|
enumPathEnds(group, group_path_count, endpoint_path_count, unique_pins, true);
|
2018-09-28 17:54:21 +02:00
|
|
|
group = async_[mm_index];
|
|
|
|
|
if (group)
|
2024-11-24 00:38:26 +01:00
|
|
|
enumPathEnds(group, group_path_count, endpoint_path_count, unique_pins, true);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathGroups::enumPathEnds(PathGroup *group,
|
2024-11-24 00:38:26 +01:00
|
|
|
int group_path_count,
|
|
|
|
|
int endpoint_path_count,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool unique_pins,
|
2018-12-05 23:18:41 +01:00
|
|
|
bool cmp_slack)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
// Insert the worst max_path path ends in the group into a path
|
|
|
|
|
// enumerator.
|
2024-11-24 00:38:26 +01:00
|
|
|
PathEnum path_enum(group_path_count, endpoint_path_count,
|
|
|
|
|
unique_pins, cmp_slack, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
PathGroupIterator *end_iter = group->iterator();
|
|
|
|
|
while (end_iter->hasNext()) {
|
|
|
|
|
PathEnd *end = end_iter->next();
|
2025-01-15 17:17:14 +01:00
|
|
|
if (group->saveable(end)
|
|
|
|
|
|| group->enumMinSlackUnderMin(end))
|
2018-09-28 17:54:21 +02:00
|
|
|
path_enum.insert(end);
|
|
|
|
|
}
|
|
|
|
|
delete end_iter;
|
|
|
|
|
group->clear();
|
|
|
|
|
|
2024-11-24 00:38:26 +01:00
|
|
|
// Parallel path enumeratation to find the endpoint_path_count/max path ends.
|
|
|
|
|
for (int n = 0; path_enum.hasNext() && n < group_path_count; n++) {
|
2018-09-28 17:54:21 +02:00
|
|
|
PathEnd *end = path_enum.next();
|
2025-01-14 00:58:47 +01:00
|
|
|
if (group->saveable(end))
|
2025-01-11 18:27:05 +01:00
|
|
|
group->insert(end);
|
2025-01-14 01:43:35 +01:00
|
|
|
else
|
|
|
|
|
delete end;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathGroups::makeGroupPathEnds(ExceptionTo *to,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
PathEndVisitor *visitor)
|
|
|
|
|
{
|
|
|
|
|
Network *network = this->network();
|
|
|
|
|
Graph *graph = this->graph();
|
|
|
|
|
Search *search = this->search();
|
|
|
|
|
if (exceptionToEmpty(to))
|
|
|
|
|
makeGroupPathEnds(search->endpoints(), corner, min_max, visitor);
|
|
|
|
|
else {
|
|
|
|
|
// Only visit -to filter pins.
|
2022-03-01 14:55:25 +01:00
|
|
|
VertexSet endpoints(graph_);
|
2023-06-30 02:06:07 +02:00
|
|
|
PinSet pins = to->allPins(network);
|
2018-09-28 17:54:21 +02:00
|
|
|
PinSet::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin = pin_iter.next();
|
|
|
|
|
Vertex *vertex, *bidirect_drvr_vertex;
|
|
|
|
|
graph->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
|
|
|
|
if (vertex
|
|
|
|
|
&& search->isEndpoint(vertex))
|
|
|
|
|
endpoints.insert(vertex);
|
|
|
|
|
if (bidirect_drvr_vertex
|
|
|
|
|
&& search->isEndpoint(bidirect_drvr_vertex))
|
|
|
|
|
endpoints.insert(bidirect_drvr_vertex);
|
|
|
|
|
}
|
|
|
|
|
makeGroupPathEnds(&endpoints, corner, min_max, visitor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
exceptionToEmpty(ExceptionTo *to)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return to == nullptr
|
|
|
|
|
|| (to->pins() == nullptr
|
|
|
|
|
&& to->instances() == nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class MakeEndpointPathEnds : public VertexVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
MakeEndpointPathEnds(PathEndVisitor *path_end_visitor,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
const StaState *sta);
|
2021-12-20 18:50:44 +01:00
|
|
|
MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends);
|
2018-09-28 17:54:21 +02:00
|
|
|
~MakeEndpointPathEnds();
|
2021-12-16 03:35:02 +01:00
|
|
|
virtual VertexVisitor *copy() const;
|
2018-09-28 17:54:21 +02:00
|
|
|
virtual void visit(Vertex *vertex);
|
|
|
|
|
|
|
|
|
|
private:
|
2024-06-27 22:57:58 +02:00
|
|
|
VisitPathEnds visit_path_ends_;
|
2018-09-28 17:54:21 +02:00
|
|
|
PathEndVisitor *path_end_visitor_;
|
|
|
|
|
const Corner *corner_;
|
|
|
|
|
const MinMaxAll *min_max_;
|
|
|
|
|
const StaState *sta_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
MakeEndpointPathEnds::MakeEndpointPathEnds(PathEndVisitor *path_end_visitor,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
const StaState *sta) :
|
2024-06-27 22:57:58 +02:00
|
|
|
visit_path_ends_(sta),
|
2018-09-28 17:54:21 +02:00
|
|
|
path_end_visitor_(path_end_visitor->copy()),
|
|
|
|
|
corner_(corner),
|
|
|
|
|
min_max_(min_max),
|
|
|
|
|
sta_(sta)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-20 18:50:44 +01:00
|
|
|
MakeEndpointPathEnds::MakeEndpointPathEnds(const MakeEndpointPathEnds &make_path_ends) :
|
2024-06-27 22:57:58 +02:00
|
|
|
visit_path_ends_(make_path_ends.sta_),
|
2021-12-20 18:50:44 +01:00
|
|
|
path_end_visitor_(make_path_ends.path_end_visitor_->copy()),
|
|
|
|
|
corner_(make_path_ends.corner_),
|
|
|
|
|
min_max_(make_path_ends.min_max_),
|
|
|
|
|
sta_(make_path_ends.sta_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
MakeEndpointPathEnds::~MakeEndpointPathEnds()
|
|
|
|
|
{
|
|
|
|
|
delete path_end_visitor_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VertexVisitor *
|
2021-12-16 03:35:02 +01:00
|
|
|
MakeEndpointPathEnds::copy() const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
return new MakeEndpointPathEnds(path_end_visitor_, corner_, min_max_, sta_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
MakeEndpointPathEnds::visit(Vertex *vertex)
|
|
|
|
|
{
|
2024-06-27 22:57:58 +02:00
|
|
|
visit_path_ends_.visitPathEnds(vertex, corner_, min_max_, true, path_end_visitor_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
PathGroups::makeGroupPathEnds(VertexSet *endpoints,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
PathEndVisitor *visitor)
|
|
|
|
|
{
|
2020-09-16 05:18:13 +02:00
|
|
|
if (thread_count_ == 1) {
|
|
|
|
|
MakeEndpointPathEnds end_visitor(visitor, corner, min_max, this);
|
|
|
|
|
for (auto endpoint : *endpoints)
|
|
|
|
|
end_visitor.visit(endpoint);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2021-12-20 18:50:44 +01:00
|
|
|
Vector<MakeEndpointPathEnds> visitors(thread_count_,
|
|
|
|
|
MakeEndpointPathEnds(visitor, corner,
|
|
|
|
|
min_max, this));
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto endpoint : *endpoints) {
|
2020-09-16 05:18:13 +02:00
|
|
|
dispatch_queue_->dispatch( [endpoint, &visitors](int i)
|
2021-12-20 18:50:44 +01:00
|
|
|
{ visitors[i].visit(endpoint); } );
|
2020-09-16 05:18:13 +02:00
|
|
|
}
|
|
|
|
|
dispatch_queue_->finishTasks();
|
2019-11-11 16:48:27 +01:00
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|