report paths in multiple matching groups resolves ascenium #145

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2025-10-30 08:53:36 -07:00
parent de0f5440a6
commit 98d801d7a6
14 changed files with 215 additions and 107 deletions

View File

@ -68,7 +68,7 @@ public:
path_delay
};
virtual PathEnd *copy() = 0;
virtual PathEnd *copy() const = 0;
virtual ~PathEnd();
void deletePath();
Path *path() { return path_; }
@ -84,6 +84,8 @@ public:
PathAPIndex pathIndex(const StaState *sta) const;
virtual void reportShort(const ReportPath *report) const = 0;
virtual void reportFull(const ReportPath *report) const = 0;
PathGroup *pathGroup() const { return path_group_; }
void setPathGroup(PathGroup *path_group);
// Predicates for PathEnd type.
// Default methods overridden by respective types.
@ -216,6 +218,7 @@ protected:
PathDelay *path_delay,
const StaState *sta);
Path *path_;
PathGroup *path_group_;
};
class PathEndUnconstrained : public PathEnd
@ -224,7 +227,7 @@ public:
explicit PathEndUnconstrained(Path *path);
virtual Type type() const;
virtual const char *typeName() const;
virtual PathEnd *copy();
virtual PathEnd *copy() const;
virtual void reportShort(const ReportPath *report) const;
virtual void reportFull(const ReportPath *report) const;
virtual bool isUnconstrained() const;
@ -321,7 +324,7 @@ public:
Path *clk_path,
MultiCyclePath *mcp,
const StaState *sta);
virtual PathEnd *copy();
virtual PathEnd *copy() const;
virtual Type type() const;
virtual const char *typeName() const;
virtual void reportShort(const ReportPath *report) const;
@ -367,7 +370,7 @@ public:
virtual bool isCheck() const { return false; }
virtual bool isLatchCheck() const { return true; }
virtual PathDelay *pathDelay() const { return path_delay_; }
virtual PathEnd *copy();
virtual PathEnd *copy() const;
Path *latchDisable();
const Path *latchDisable() const;
virtual void reportShort(const ReportPath *report) const;
@ -428,7 +431,7 @@ public:
Path *clk_path,
MultiCyclePath *mcp,
const StaState *sta);
virtual PathEnd *copy();
virtual PathEnd *copy() const;
virtual Type type() const;
virtual const char *typeName() const;
virtual void reportShort(const ReportPath *report) const;
@ -474,7 +477,7 @@ public:
MultiCyclePath *mcp,
ArcDelay margin,
const StaState *sta);
virtual PathEnd *copy();
virtual PathEnd *copy() const;
virtual Type type() const;
virtual const char *typeName() const;
virtual void reportShort(const ReportPath *report) const;
@ -506,7 +509,7 @@ public:
Path *data_clk_path,
MultiCyclePath *mcp,
const StaState *sta);
virtual PathEnd *copy();
virtual PathEnd *copy() const;
virtual Type type() const;
virtual const char *typeName() const;
virtual void reportShort(const ReportPath *report) const;
@ -560,7 +563,7 @@ public:
Path *path,
OutputDelay *output_delay,
const StaState *sta);
virtual PathEnd *copy();
virtual PathEnd *copy() const;
virtual Type type() const;
virtual const char *typeName() const;
virtual void reportShort(const ReportPath *report) const;

View File

@ -40,6 +40,8 @@ class PathEndVisitor;
typedef PathEndSeq::Iterator PathGroupIterator;
typedef Map<const Clock*, PathGroup*> PathGroupClkMap;
typedef Map<const char*, PathGroup*, CharPtrLess> PathGroupNamedMap;
typedef std::vector<PathGroup*> PathGroupSeq;
typedef std::vector<std::string> StdStringSeq;
// A collection of PathEnds grouped and sorted for reporting.
class PathGroup
@ -133,8 +135,8 @@ public:
const MinMax *min_max) const;
PathGroup *findPathGroup(const Clock *clock,
const MinMax *min_max) const;
PathGroup *pathGroup(const PathEnd *path_end) const;
static std::string pathGroupName(const PathEnd *path_end,
PathGroupSeq pathGroups(const PathEnd *path_end) const;
static StdStringSeq pathGroupNames(const PathEnd *path_end,
const StaState *sta);
static const char *asyncPathGroupName() { return async_group_name_; }
static const char *pathDelayGroupName() { return path_delay_group_name_; }
@ -179,8 +181,6 @@ protected:
const MinMax *min_max);
bool reportGroup(const char *group_name,
PathGroupNameSet *group_names) const;
static GroupPath *groupPathTo(const PathEnd *path_end,
const StaState *sta);
int group_path_count_;
int endpoint_path_count_;

View File

@ -996,6 +996,12 @@ public:
const MinMax *min_max,
bool match_min_max_exactly,
bool require_to_pin) const;
void groupPathsTo(const Pin *pin,
const RiseFall *rf,
const ClockEdge *clk_edge,
const MinMax *min_max,
// Return value.
ExceptionPathSeq &group_paths) const;
bool isCompleteTo(ExceptionState *state,
const Pin *pin,
const RiseFall *rf,
@ -1164,6 +1170,13 @@ protected:
// Return values.
ExceptionPath *&hi_priority_exception,
int &hi_priority) const;
void groupPathsTo(const ExceptionPathSet *to_exceptions,
const Pin *pin,
const RiseFall *rf,
const ClockEdge *clk_edge,
const MinMax *min_max,
// Return value.
ExceptionPathSeq &group_paths) const;
void makeLoopPath(ExceptionThruSeq *thrus);
void makeLoopException(const Pin *loop_input_pin,
const Pin *loop_pin,

View File

@ -71,6 +71,8 @@ typedef Map<Vertex*, Slack> VertexSlackMap;
typedef Vector<VertexSlackMap> VertexSlackMapSeq;
typedef Vector<WorstSlacks> WorstSlacksSeq;
typedef std::vector<DelayDbl> DelayDblSeq;
typedef Vector<ExceptionPath*> ExceptionPathSeq;
typedef std::vector<PathGroup*> PathGroupSeq;
class Search : public StaState
{
@ -165,7 +167,7 @@ public:
// Clock arrival at the path source/launch point.
Arrival pathClkPathArrival(const Path *path) const;
PathGroup *pathGroup(const PathEnd *path_end) const;
PathGroupSeq pathGroups(const PathEnd *path_end) const;
void deletePathGroups();
void makePathGroups(int group_path_count,
int endpoint_path_count,
@ -187,6 +189,7 @@ public:
const MinMax *min_max,
bool match_min_max_exactly,
bool require_to_pin) const;
ExceptionPathSeq groupPathsTo(const PathEnd *path_end) const;
FilterPath *filter() const { return filter_; }
void deleteFilter();
void deleteFilteredArrivals();

View File

@ -5362,6 +5362,8 @@ Sdc::exceptionThruStates(const ExceptionPathSet *exceptions,
}
}
////////////////////////////////////////////////////////////////
void
Sdc::exceptionTo(ExceptionPathType type,
const Pin *pin,
@ -5484,6 +5486,47 @@ Sdc::isCompleteTo(ExceptionState *state,
////////////////////////////////////////////////////////////////
void
Sdc::groupPathsTo(const Pin *pin,
const RiseFall *rf,
const ClockEdge *clk_edge,
const MinMax *min_max,
// Return value.
ExceptionPathSeq &group_paths) const
{
if (!first_to_inst_exceptions_.empty()) {
Instance *inst = network_->instance(pin);
groupPathsTo(first_to_inst_exceptions_.findKey(inst), pin, rf,
clk_edge, min_max, group_paths);
}
if (!first_to_pin_exceptions_.empty())
groupPathsTo(first_to_pin_exceptions_.findKey(pin), pin, rf,
clk_edge, min_max, group_paths);
if (clk_edge && !first_to_clk_exceptions_.empty())
groupPathsTo(first_to_clk_exceptions_.findKey(clk_edge->clock()),
pin, rf, clk_edge, min_max, group_paths);
}
void
Sdc::groupPathsTo(const ExceptionPathSet *to_exceptions,
const Pin *pin,
const RiseFall *rf,
const ClockEdge *clk_edge,
const MinMax *min_max,
// Return value.
ExceptionPathSeq &group_paths) const
{
if (to_exceptions) {
for (ExceptionPath *exception : *to_exceptions) {
if (exception->isGroupPath()
&& exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, true, false))
group_paths.push_back(exception);
}
}
}
////////////////////////////////////////////////////////////////
Wireload *
Sdc::wireload(const MinMax *min_max)
{

View File

@ -306,7 +306,7 @@ Path::isClock(const StaState *sta) const
const MinMax *
Path::minMax(const StaState *sta) const
{
return pathAnalysisPt(sta)->pathMinMax();
return tag(sta)->minMax(sta);
}
PathAPIndex

View File

@ -49,7 +49,8 @@
namespace sta {
PathEnd::PathEnd(Path *path) :
path_(path)
path_(path),
path_group_(nullptr)
{
}
@ -65,6 +66,12 @@ PathEnd::setPath(Path *path)
path_ = path;
}
void
PathEnd::setPathGroup(PathGroup *path_group)
{
path_group_ = path_group;
}
Vertex *
PathEnd::vertex(const StaState *sta) const
{
@ -454,7 +461,7 @@ PathEndUnconstrained::PathEndUnconstrained(Path *path) :
}
PathEnd *
PathEndUnconstrained::copy()
PathEndUnconstrained::copy() const
{
return new PathEndUnconstrained(path_);
}
@ -955,7 +962,7 @@ PathEndCheck::PathEndCheck(Path *path,
}
PathEnd *
PathEndCheck::copy()
PathEndCheck::copy() const
{
return new PathEndCheck(path_, check_arc_, check_edge_,
clk_path_, mcp_, crpr_, crpr_valid_);
@ -1126,7 +1133,7 @@ PathEndLatchCheck::PathEndLatchCheck(Path *path,
}
PathEnd *
PathEndLatchCheck::copy()
PathEndLatchCheck::copy() const
{
return new PathEndLatchCheck(path_, check_arc_, check_edge_,
clk_path_, disable_path_, mcp_, path_delay_,
@ -1346,7 +1353,7 @@ PathEndOutputDelay::PathEndOutputDelay(OutputDelay *output_delay,
}
PathEnd *
PathEndOutputDelay::copy()
PathEndOutputDelay::copy() const
{
return new PathEndOutputDelay(output_delay_, path_, clk_path_,
mcp_, crpr_, crpr_valid_);
@ -1552,7 +1559,7 @@ PathEndGatedClock::PathEndGatedClock(Path *gating_ref,
}
PathEnd *
PathEndGatedClock::copy()
PathEndGatedClock::copy() const
{
return new PathEndGatedClock(path_, clk_path_, check_role_,
mcp_, margin_, crpr_, crpr_valid_);
@ -1668,7 +1675,7 @@ PathEndDataCheck::PathEndDataCheck(DataCheck *check,
}
PathEnd *
PathEndDataCheck::copy()
PathEndDataCheck::copy() const
{
return new PathEndDataCheck(check_, path_, data_clk_path_,
clk_path_, mcp_, crpr_, crpr_valid_);
@ -1825,7 +1832,7 @@ PathEndPathDelay::PathEndPathDelay(PathDelay *path_delay,
}
PathEnd *
PathEndPathDelay::copy()
PathEndPathDelay::copy() const
{
return new PathEndPathDelay(path_delay_, path_, clk_path_,
check_arc_, check_edge_, output_delay_,

View File

@ -168,6 +168,7 @@ PathGroup::insert(PathEnd *path_end)
{
LockGuard lock(lock_);
path_ends_.push_back(path_end);
path_end->setPathGroup(this);
if (group_path_count_ != group_path_count_max
&& path_ends_.size() > group_path_count_ * 2)
prune();
@ -398,21 +399,27 @@ PathGroups::reportGroup(const char *group_name,
|| group_names->hasKey(group_name);
}
PathGroup *
PathGroups::pathGroup(const PathEnd *path_end) const
PathGroupSeq
PathGroups::pathGroups(const PathEnd *path_end) const
{
PathGroupSeq path_groups;
PathGroup *path_group = nullptr;
ExceptionPathSeq group_paths = search_->groupPathsTo(path_end);
const MinMax *min_max = path_end->minMax(this);
int mm_index = min_max->index();
GroupPath *group_path = groupPathTo(path_end, this);
if (path_end->isUnconstrained())
return unconstrained_[mm_index];
path_group = unconstrained_[mm_index];
// GroupPaths have precedence.
else if (group_path) {
else if (!group_paths.empty()) {
for (ExceptionPath *group_path : group_paths) {
if (group_path->isDefault())
return path_delay_[mm_index];
path_groups.push_back(path_delay_[mm_index]);
else {
const char *group_name = group_path->name();
return findPathGroup(group_name, min_max);
PathGroup *group = findPathGroup(group_name, min_max);
if (group)
path_groups.push_back(group);
}
}
}
else if (path_end->isCheck() || path_end->isLatchCheck()) {
@ -420,15 +427,15 @@ PathGroups::pathGroup(const PathEnd *path_end) const
const Clock *tgt_clk = path_end->targetClk(this);
if (check_role == TimingRole::removal()
|| check_role == TimingRole::recovery())
return async_[mm_index];
path_group = async_[mm_index];
else
return findPathGroup(tgt_clk, min_max);
path_group = findPathGroup(tgt_clk, min_max);
}
else if (path_end->isOutputDelay()
|| path_end->isDataCheck())
return findPathGroup(path_end->targetClk(this), min_max);
path_group = findPathGroup(path_end->targetClk(this), min_max);
else if (path_end->isGatedClock())
return gated_clk_[mm_index];
path_group = 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.
@ -436,45 +443,52 @@ PathGroups::pathGroup(const PathEnd *path_end) const
const Clock *tgt_clk = path_end->targetClk(this);
if (tgt_clk
&& !path_delay->ignoreClkLatency())
return findPathGroup(tgt_clk, min_max);
path_group = findPathGroup(tgt_clk, min_max);
else
return path_delay_[mm_index];
}
else {
report_->critical(1390, "unknown path end type");
return nullptr;
path_group = path_delay_[mm_index];
}
if (path_group)
path_groups.push_back(path_group);
return path_groups;
}
// Mirrors PathGroups::pathGroup.
std::string
PathGroups::pathGroupName(const PathEnd *path_end,
StdStringSeq
PathGroups::pathGroupNames(const PathEnd *path_end,
const StaState *sta)
{
GroupPath *group_path = groupPathTo(path_end, sta);
StdStringSeq group_names;
const char *group_name = nullptr;
const Search *search = sta->search();
ExceptionPathSeq group_paths = search->groupPathsTo(path_end);
if (path_end->isUnconstrained())
return unconstrained_group_name_;
group_name = unconstrained_group_name_;
else if (!group_paths.empty()) {
// GroupPaths have precedence.
else if (group_path) {
for (ExceptionPath *group_path : group_paths) {
if (group_path->isDefault())
return path_delay_group_name_;
group_names.push_back(path_delay_group_name_);
else
return group_path->name();
group_names.push_back(group_path->name());
}
}
else if (path_end->isCheck() || path_end->isLatchCheck()) {
const TimingRole *check_role = path_end->checkRole(sta);
const Clock *tgt_clk = path_end->targetClk(sta);
if (check_role == TimingRole::removal()
|| check_role == TimingRole::recovery())
return async_group_name_;
group_name = async_group_name_;
else
return tgt_clk->name();
group_name = tgt_clk->name();
}
else if (path_end->isOutputDelay()
|| path_end->isDataCheck())
return path_end->targetClk(sta)->name();
|| path_end->isDataCheck()) {
const Clock *tgt_clk = path_end->targetClk(sta);
if (tgt_clk)
group_name = tgt_clk->name();
}
else if (path_end->isGatedClock())
return gated_clk_group_name_;
group_name = gated_clk_group_name_;
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.
@ -482,28 +496,13 @@ PathGroups::pathGroupName(const PathEnd *path_end,
const Clock *tgt_clk = path_end->targetClk(sta);
if (tgt_clk
&& !path_delay->ignoreClkLatency())
return tgt_clk->name();
group_name = tgt_clk->name();
else
return path_delay_group_name_;
group_name = path_delay_group_name_;
}
else {
sta->report()->critical(1391, "unknown path end type");
return nullptr;
}
}
GroupPath *
PathGroups::groupPathTo(const PathEnd *path_end,
const StaState *sta)
{
const Path *path = path_end->path();
const Pin *pin = path->pin(sta);
ExceptionPath *exception =
sta->search()->exceptionTo(ExceptionPathType::group_path, path,
pin, path->transition(sta),
path_end->targetClkEdge(sta),
path->minMax(sta), false, false);
return dynamic_cast<GroupPath*>(exception);
if (group_name)
group_names.push_back(group_name);
return group_names;
}
void
@ -632,8 +631,7 @@ MakePathEnds1::copy() const
void
MakePathEnds1::visit(PathEnd *path_end)
{
PathGroup *group = path_groups_->pathGroup(path_end);
if (group)
for (PathGroup *group : path_groups_->pathGroups(path_end))
visitPathEnd(path_end, group);
}
@ -644,14 +642,15 @@ MakePathEnds1::visitPathEnd(PathEnd *path_end,
if (group->saveable(path_end)) {
// Only keep the path end with the smallest slack/latest arrival.
PathEnd *worst_end = ends_.findKey(group);
PathEnd *copy = path_end->copy();
if (worst_end) {
if (cmp_(path_end, worst_end)) {
ends_[group] = path_end->copy();
ends_[group] = copy;
delete worst_end;
}
}
else
ends_[group] = path_end->copy();
ends_[group] = copy;
}
}
@ -732,8 +731,7 @@ MakePathEndsAll::~MakePathEndsAll()
void
MakePathEndsAll::visit(PathEnd *path_end)
{
PathGroup *group = path_groups_->pathGroup(path_end);
if (group)
for (PathGroup *group : path_groups_->pathGroups(path_end))
visitPathEnd(path_end, group);
}

View File

@ -401,8 +401,8 @@ ReportPath::reportEndpointHeader(const PathEnd *end,
{
PathGroup *prev_group = nullptr;
if (prev_end)
prev_group = search_->pathGroup(prev_end);
PathGroup *group = search_->pathGroup(end);
prev_group = prev_end->pathGroup();
PathGroup *group = end->pathGroup();
if (group && group != prev_group) {
if (prev_group)
reportBlankLine();
@ -1086,7 +1086,7 @@ ReportPath::reportJson(const PathEnd *end,
result += "{\n";
stringAppend(result, " \"type\": \"%s\",\n", end->typeName());
stringAppend(result, " \"path_group\": \"%s\",\n",
search_->pathGroup(end)->name());
end->pathGroup()->name());
stringAppend(result, " \"path_type\": \"%s\",\n",
end->minMax(this)->to_string().c_str());
@ -1273,7 +1273,7 @@ ReportPath::reportSlackOnly(const PathEnd *end) const
{
string line;
const EarlyLate *early_late = end->pathEarlyLate(this);
reportDescription(search_->pathGroup(end)->name(), line);
reportDescription(end->pathGroup()->name(), line);
if (end->isUnconstrained())
reportSpaceFieldDelay(end->dataArrivalTimeOffset(this), early_late, line);
else
@ -1939,7 +1939,7 @@ ReportPath::reportGroup(const PathEnd *end) const
{
string line;
line = "Path Group: ";
PathGroup *group = search_->pathGroup(end);
PathGroup *group = end->pathGroup();
line += group ? group->name() : "(none)";
report_->reportLineString(line);

View File

@ -3767,6 +3767,34 @@ Search::exceptionTo(ExceptionPathType type,
////////////////////////////////////////////////////////////////
// Find group paths that end at the path.
ExceptionPathSeq
Search::groupPathsTo(const PathEnd *path_end) const
{
ExceptionPathSeq group_paths;
const Path *path = path_end->path();
const Pin *pin = path->pin(this);
const Tag *tag = path->tag(this);
const RiseFall *rf = tag->transition();
const ClockEdge *clk_edge = path_end->targetClkEdge(this);
const MinMax *min_max = tag->minMax(this);
const ExceptionStateSet *states = path->tag(this)->states();
if (states) {
for (auto state : *states) {
ExceptionPath *exception = state->exception();
if (exception->isGroupPath()
&& sdc_->exceptionMatchesTo(exception, pin, rf, clk_edge, min_max,
false, false))
group_paths.push_back(exception);
}
}
// Check for group_path -to exceptions originating at the end pin or target clock.
sdc_->groupPathsTo(pin, rf, clk_edge, min_max, group_paths);
return group_paths;
}
////////////////////////////////////////////////////////////////
Slack
Search::totalNegativeSlack(const MinMax *min_max)
{
@ -4095,13 +4123,13 @@ Search::deletePathGroups()
path_groups_ = nullptr;
}
PathGroup *
Search::pathGroup(const PathEnd *path_end) const
PathGroupSeq
Search::pathGroups(const PathEnd *path_end) const
{
if (path_groups_)
return path_groups_->pathGroup(path_end);
return path_groups_->pathGroups(path_end);
else
return nullptr;
return PathGroupSeq();
}
bool

View File

@ -3068,13 +3068,17 @@ EndpointPathEndVisitor::copy() const
void
EndpointPathEndVisitor::visit(PathEnd *path_end)
{
if (path_end->minMax(sta_) == min_max_
&& PathGroups::pathGroupName(path_end, sta_) == path_group_name_) {
if (path_end->minMax(sta_) == min_max_) {
StdStringSeq group_names = PathGroups::pathGroupNames(path_end, sta_);
for (std::string &group_name : group_names) {
if (group_name == path_group_name_) {
Slack end_slack = path_end->slack(sta_);
if (delayLess(end_slack, slack_, sta_))
slack_ = end_slack;
}
}
}
}
Slack
Sta::endpointSlack(const Pin *pin,

View File

@ -181,6 +181,12 @@ 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
{

View File

@ -73,6 +73,7 @@ public:
const Pin *clkSrc() const;
int rfIndex() const { return rf_index_; }
const RiseFall *transition() const;
const MinMax *minMax(const StaState *sta) const;
PathAnalysisPt *pathAnalysisPt(const StaState *sta) const;
PathAPIndex pathAPIndex() const { return path_ap_index_; }
TagIndex index() const { return index_; }

View File

@ -177,7 +177,8 @@ VisitPathGroupEnds::vertexBegin(Vertex *)
void
VisitPathGroupEnds::visit(PathEnd *path_end)
{
PathGroup *group = sta_->search()->pathGroup(path_end);
PathGroupSeq groups = sta_->search()->pathGroups(path_end);
for (PathGroup *group : groups) {
if (group == path_group_) {
Path *path = path_end->path();
Vertex *vertex = path->vertex(sta_);
@ -185,6 +186,7 @@ VisitPathGroupEnds::visit(PathEnd *path_end)
vertex_matches_ = true;
}
}
}
static void
vertexPathSetMapInsertPath(VertexPathSetMap *matching_path_map,