dynamic loop do not use search pred

Signed-off-by: James Cherry <cherry@CerezoBook.local>
This commit is contained in:
James Cherry 2026-06-24 16:55:51 -07:00
parent f1adf5b26a
commit e603b7abe0
5 changed files with 140 additions and 93 deletions

View File

@ -585,7 +585,8 @@ Graph::visitFanouts(Vertex *vertex,
edge;
edge = this->edge(edge->vertex_out_next_)) {
Vertex *to_vertex = this->vertex(edge->to_);
if (pred->searchThru(edge) && pred->searchTo(to_vertex))
if (pred->searchThru(edge)
&& pred->searchTo(to_vertex))
fn(to_vertex);
}
}
@ -601,7 +602,8 @@ Graph::visitFanoutEdges(Vertex *vertex,
edge;
edge = this->edge(edge->vertex_out_next_)) {
Vertex *to_vertex = this->vertex(edge->to_);
if (pred->searchThru(edge) && pred->searchTo(to_vertex))
if (pred->searchThru(edge)
&& pred->searchTo(to_vertex))
fn(edge, to_vertex);
}
}
@ -617,7 +619,8 @@ Graph::visitFanins(Vertex *vertex,
edge;
edge = this->edge(edge->vertex_in_next_)) {
Vertex *from_vertex = this->vertex(edge->from_);
if (pred->searchThru(edge) && pred->searchFrom(from_vertex))
if (pred->searchThru(edge)
&& pred->searchFrom(from_vertex))
fn(from_vertex);
}
}
@ -633,7 +636,8 @@ Graph::visitFaninEdges(Vertex *vertex,
edge;
edge = this->edge(edge->vertex_in_next_)) {
Vertex *from_vertex = this->vertex(edge->from_);
if (pred->searchThru(edge) && pred->searchFrom(from_vertex))
if (pred->searchThru(edge)
&& pred->searchFrom(from_vertex))
fn(edge, from_vertex);
}
}

View File

@ -284,8 +284,8 @@ public:
Vertex *vertex,
const Mode *mode,
TagGroupBldr *tag_bldr);
void enqueueLatchDataOutputs(Vertex *vertex);
void enqueueLatchOutput(Vertex *vertex);
void postponeLatchDataOutputs(Vertex *vertex);
void postponeArrivals(Vertex *vertex);
void enqueuePendingClkFanouts();
void postponeClkFanouts(Vertex *vertex);
void seedRequired(Vertex *vertex);
@ -590,7 +590,7 @@ protected:
// Search predicates.
SearchPred *search_thru_;
SearchAdj *search_adj_;
SearchPred *search_adj_;
EvalPred *eval_pred_;
// Some arrivals exist.
@ -647,11 +647,11 @@ protected:
std::mutex tag_group_lock_;
// Latches data outputs to queue on the next search pass.
VertexSet pending_latch_outputs_;
std::mutex pending_latch_outputs_lock_;
VertexSet postponed_arrivals_;
std::mutex postponed_arrivals_lock_;
// Clock network endpoints where arrival search was suppended by findClkArrivals().
VertexSet pending_clk_endpoints_;
std::mutex pending_clk_endpoints_lock_;
VertexSet postponed_clk_endpoints_;
std::mutex postponed_clk_endpoints_lock_;
VertexSet endpoints_;
bool endpoints_initialized_{false};
@ -802,6 +802,8 @@ protected:
void pruneCrprArrivals();
void constrainedRequiredsInvalid(Vertex *vertex,
bool is_clk);
bool hasPendingLoopPaths(Edge *edge) const;
bool always_to_endpoints_;
bool always_save_prev_paths_;
bool clks_only_;

View File

@ -51,19 +51,19 @@ public:
// Search is allowed from from_vertex.
virtual bool searchFrom(const Vertex *from_vertex,
const Mode *mode) const = 0;
bool searchFrom(const Vertex *from_vertex) const;
virtual bool searchFrom(const Vertex *from_vertex) const;
// Search is allowed through edge.
// from/to pins are NOT checked.
// inst can be either the from_pin or to_pin instance because it
// is only referenced when they are the same (non-wire edge).
virtual bool searchThru(Edge *edge,
const Mode *mode) const = 0;
bool searchThru(Edge *edge) const;
virtual bool searchThru(Edge *edge) const;
// Search is allowed to to_pin.
virtual bool searchTo(const Vertex *to_vertex,
const Mode *mode) const = 0;
bool searchTo(const Vertex *to_vertex) const;
void copyState(const StaState *sta);
virtual bool searchTo(const Vertex *to_vertex) const;
virtual void copyState(const StaState *sta);
protected:
const StaState *sta_;

View File

@ -238,6 +238,28 @@ PathEnum::reportDiversionPath(Diversion *div)
using VisitedFanins = std::set<std::pair<const Vertex *, const TimingArc *>>;
using VertexEdge = std::pair<const Vertex *, const RiseFall *>;
class EnumPred : public EvalPred
{
public:
EnumPred(const StaState *sta);
bool searchThru(Edge *edge,
const Mode *mode) const override;
};
EnumPred::EnumPred(const StaState *sta) :
EvalPred(sta)
{
}
bool
EnumPred::searchThru(Edge *edge,
const Mode *mode) const
{
// Do not enum paths across disabled loop edges.
return EvalPred::searchThru(edge, mode)
&& !edge->isDisabledLoop();
}
class PathEnumFaninVisitor : public PathVisitor
{
public:
@ -247,6 +269,7 @@ public:
bool unique_edges,
PathEnum *path_enum);
PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor);
virtual ~PathEnumFaninVisitor();
VertexVisitor *copy() const override;
void visitFaninPathsThru(Path *before_div,
Vertex *prev_vertex,
@ -311,7 +334,7 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(PathEnd *path_end,
bool unique_pins,
bool unique_edges,
PathEnum *path_enum) :
PathVisitor(path_enum),
PathVisitor(new EnumPred(path_enum), false, path_enum),
path_end_(path_end),
before_div_(before_div),
unique_pins_(unique_pins),
@ -334,6 +357,11 @@ PathEnumFaninVisitor::PathEnumFaninVisitor(const PathEnumFaninVisitor &visitor)
{
}
PathEnumFaninVisitor::~PathEnumFaninVisitor()
{
delete pred_;
}
VertexVisitor *
PathEnumFaninVisitor::copy() const
{
@ -655,10 +683,10 @@ PathEnum::makeDivertedPath(Path *path,
Path *prev_copy = nullptr;
while (p) {
// prev_path made in next pass.
Path *copy =
new Path(p->vertex(this), p->tag(this), p->arrival(),
// Replaced on next pass.
p->prevPath(), p->prevEdge(this), p->prevArc(this), true, this);
Path *copy = new Path(p->vertex(this), p->tag(this), p->arrival(),
// Replaced on next pass.
p->prevPath(), p->prevEdge(this), p->prevArc(this),
true, this);
search_->saveEnumPath(copy);
if (prev_copy)
prev_copy->setPrevPath(copy);

View File

@ -95,8 +95,8 @@ EvalPred::searchThru(Edge *edge,
{
const TimingRole *role = edge->role();
return SearchPred0::searchThru(edge, mode)
&& (sta_->variables()->dynamicLoopBreaking() || !edge->isDisabledLoop())
&& (search_thru_latches_ || role->isLatchDtoQ()
&& (search_thru_latches_
|| role->isLatchDtoQ()
|| sta_->latches()->latchDtoQState(edge, mode) == LatchEnableState::open);
}
@ -133,54 +133,56 @@ bool
SearchThru::searchThru(Edge *edge,
const Mode *mode) const
{
return EvalPred::searchThru(edge, mode) && !edge->role()->isLatchDtoQ();
return EvalPred::searchThru(edge, mode)
&& !edge->role()->isLatchDtoQ();
}
////////////////////////////////////////////////////////////////
// SearchAdj is mode independent. Search unless
// disabled to break combinational loop
// latch D->Q edge
// timing check edge
// dynamic loop breaking pending tags
// register set/clear
// bidirect load->driver
class SearchAdj : public SearchPred
{
public:
SearchAdj(TagGroupBldr *tag_bldr,
const StaState *sta);
SearchAdj(const StaState *sta);
bool searchFrom(const Vertex *from_vertex) const override;
bool searchFrom(const Vertex *from_vertex,
const Mode *mode) const override;
bool searchThru(Edge *edge) const override;
bool searchThru(Edge *edge,
const Mode *mode) const override;
bool searchTo(const Vertex *to_vertex) const override;
bool searchTo(const Vertex *to_vertex,
const Mode *mode) const override;
protected:
bool loopEnabled(Edge *edge) const;
bool hasPendingLoopPaths(Edge *edge) const;
TagGroupBldr *tag_bldr_;
const StaState *sta_;
};
SearchAdj::SearchAdj(TagGroupBldr *tag_bldr,
const StaState *sta) :
SearchAdj::SearchAdj(const StaState *sta) :
SearchPred(sta),
tag_bldr_(tag_bldr),
sta_(sta)
{
}
bool
SearchAdj::searchFrom(const Vertex * /* from_vertex */,
SearchAdj::searchFrom(const Vertex *) const
{
return true;
}
bool
SearchAdj::searchFrom(const Vertex *,
const Mode *) const
{
return true;
}
bool
SearchAdj::searchThru(Edge *edge,
const Mode *) const
SearchAdj::searchThru(Edge *edge) const
{
const TimingRole *role = edge->role();
const Variables *variables = sta_->variables();
@ -189,42 +191,24 @@ SearchAdj::searchThru(Edge *edge,
// Register/latch preset/clr edges are disabled by default.
|| (role == TimingRole::regSetClr()
&& !variables->presetClrArcsEnabled())
|| sta_->isDisabledBidirectInstPath(edge)
|| (edge->isDisabledLoop()
&& !(variables->dynamicLoopBreaking() && hasPendingLoopPaths(edge))));
|| sta_->isDisabledBidirectInstPath(edge));
}
bool
SearchAdj::loopEnabled(Edge *edge) const
SearchAdj::searchThru(Edge *edge,
const Mode *) const
{
return !edge->isDisabledLoop()
|| (sta_->variables()->dynamicLoopBreaking() && hasPendingLoopPaths(edge));
return searchThru(edge);
}
bool
SearchAdj::hasPendingLoopPaths(Edge *edge) const
SearchAdj::searchTo(const Vertex *) const
{
if (tag_bldr_ && tag_bldr_->hasLoopTag()) {
const Graph *graph = sta_->graph();
Search *search = sta_->search();
Vertex *from_vertex = edge->from(graph);
TagGroup *prev_tag_group = search->tagGroup(from_vertex);
for (auto const [from_tag, path_index] : tag_bldr_->pathIndexMap()) {
if (from_tag->isLoop()) {
// Loop false path exceptions apply to rise/fall edges so to_rf
// does not matter.
Tag *to_tag = search->thruTag(from_tag, edge, RiseFall::rise(), nullptr);
if (to_tag
&& (prev_tag_group == nullptr || !prev_tag_group->hasTag(from_tag)))
return true;
}
}
}
return false;
return true;
}
bool
SearchAdj::searchTo(const Vertex * /* to_vertex */,
SearchAdj::searchTo(const Vertex *,
const Mode *) const
{
return true;
@ -236,8 +220,7 @@ Search::Search(StaState *sta) :
StaState(sta),
search_thru_(new SearchThru(this)),
search_adj_(new SearchAdj(nullptr,
this)),
search_adj_(new SearchAdj(this)),
eval_pred_(new EvalPred(this)),
invalid_arrivals_(makeVertexSet(this)),
@ -247,9 +230,7 @@ Search::Search(StaState *sta) :
arrival_visitor_(new ArrivalVisitor(this)),
invalid_requireds_(makeVertexSet(this)),
required_iter_(new BfsBkwdIterator(BfsIndex::required,
search_adj_,
this)),
required_iter_(new BfsBkwdIterator(BfsIndex::required, search_adj_, this)),
invalid_tns_(makeVertexSet(this)),
clk_info_set_(new ClkInfoSet(ClkInfoLess(this))),
@ -261,8 +242,8 @@ Search::Search(StaState *sta) :
tag_group_capacity_(tag_capacity_),
tag_groups_(new TagGroup *[tag_group_capacity_]),
tag_group_set_(new TagGroupSet(tag_group_capacity_)),
pending_latch_outputs_(makeVertexSet(this)),
pending_clk_endpoints_(makeVertexSet(this)),
postponed_arrivals_(makeVertexSet(this)),
postponed_clk_endpoints_(makeVertexSet(this)),
endpoints_(makeVertexSet(this)),
invalid_endpoints_(makeVertexSet(this)),
@ -326,8 +307,8 @@ Search::clear()
deletePathGroups();
deletePaths();
deleteTags();
pending_latch_outputs_.clear();
pending_clk_endpoints_.clear();
postponed_arrivals_.clear();
postponed_clk_endpoints_.clear();
deleteFilter();
found_downstream_clk_pins_ = false;
}
@ -655,10 +636,10 @@ Search::findFilteredArrivals(bool thru_latches)
int arrival_count = arrival_iter_->visitParallel(max_level, arrival_visitor_);
debugPrint(debug_, "search", 1, "found {} arrivals", arrival_count);
have_pending_latch_outputs = !pending_latch_outputs_.empty();
for (Vertex *latch_output : pending_latch_outputs_)
have_pending_latch_outputs = !postponed_arrivals_.empty();
for (Vertex *latch_output : postponed_arrivals_)
arrival_visitor_->visit(latch_output, true);
pending_latch_outputs_.clear();
postponed_arrivals_.clear();
deleteTagsPrev();
}
@ -997,29 +978,30 @@ Search::findAllArrivals(bool thru_latches,
findArrivals1(levelize_->maxLevel());
have_pending_latch_outputs = !pending_latch_outputs_.empty();
for (Vertex *latch_output : pending_latch_outputs_)
have_pending_latch_outputs = !postponed_arrivals_.empty();
for (Vertex *latch_output : postponed_arrivals_)
arrival_visitor_->visit(latch_output, true);
pending_latch_outputs_.clear();
postponed_arrivals_.clear();
}
}
// Pick up where the search stopped at the clock network boundary.
void
Search::enqueuePendingClkFanouts()
{
for (Vertex *vertex : pending_clk_endpoints_) {
for (Vertex *vertex : postponed_clk_endpoints_) {
debugPrint(debug_, "search", 2, "enqueue clk fanout {}",
vertex->to_string(this));
arrival_iter_->enqueueAdjacentVertices(vertex, search_adj_);
}
pending_clk_endpoints_.clear();
postponed_clk_endpoints_.clear();
}
void
Search::postponeClkFanouts(Vertex *vertex)
{
LockGuard lock(pending_clk_endpoints_lock_);
pending_clk_endpoints_.insert(vertex);
LockGuard lock(postponed_clk_endpoints_lock_);
postponed_clk_endpoints_.insert(vertex);
}
void
@ -1089,7 +1071,7 @@ ArrivalVisitor::init0()
{
tag_bldr_ = new TagGroupBldr(true, this);
tag_bldr_no_crpr_ = new TagGroupBldr(false, this);
adj_pred_ = new SearchAdj(tag_bldr_, this);
adj_pred_ = new SearchAdj(this);
}
void
@ -1133,7 +1115,7 @@ void
ArrivalVisitor::visit(Vertex *vertex)
{
if (network_->isLatchOutput(vertex->pin()))
search_->enqueueLatchOutput(vertex);
search_->postponeArrivals(vertex);
else
visit(vertex, false);
}
@ -1167,15 +1149,25 @@ ArrivalVisitor::visit(Vertex *vertex,
// previous eval pass enqueue the latch outputs to be re-evaled on the
// next pass.
if (arrivals_changed && network_->isLatchData(pin))
search_->enqueueLatchDataOutputs(vertex);
search_->postponeLatchDataOutputs(vertex);
if ((always_to_endpoints_ || arrivals_changed)) {
if (clks_only_ && vertex->isRegClk()) {
debugPrint(debug_, "search", 3, "postponing clk fanout");
search_->postponeClkFanouts(vertex);
}
else
search_->arrivalIterator()->enqueueAdjacentVertices(vertex, adj_pred_);
else {
graph_->visitFanoutEdges(vertex, adj_pred_,
[this] (Edge *edge,
Vertex *fanout) {
if (edge->isDisabledLoop()) {
if (hasPendingLoopPaths(edge))
search_->postponeArrivals(fanout);
}
else
search_->arrivalIterator()->enqueue(fanout);
});
}
}
if (arrivals_changed) {
debugPrint(debug_, "search", 4, "arrivals changed");
@ -1185,6 +1177,26 @@ ArrivalVisitor::visit(Vertex *vertex,
}
}
bool
ArrivalVisitor::hasPendingLoopPaths(Edge *edge) const
{
if (tag_bldr_ && tag_bldr_->hasLoopTag()) {
Vertex *from_vertex = edge->from(graph_);
TagGroup *prev_tag_group = search_->tagGroup(from_vertex);
for (auto const [from_tag, path_index] : tag_bldr_->pathIndexMap()) {
if (from_tag->isLoop()) {
// Loop false path exceptions apply to rise/fall edges so to_rf
// does not matter.
Tag *to_tag = search_->thruTag(from_tag, edge, RiseFall::rise(), nullptr);
if (to_tag
&& (prev_tag_group == nullptr || !prev_tag_group->hasTag(from_tag)))
return true;
}
}
}
return false;
}
void
ArrivalVisitor::seedArrivals(Vertex *vertex)
{
@ -1385,24 +1397,24 @@ ArrivalVisitor::pruneCrprArrivals()
}
void
Search::enqueueLatchDataOutputs(Vertex *latch_data)
Search::postponeLatchDataOutputs(Vertex *latch_data)
{
VertexOutEdgeIterator edge_iter(latch_data, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role() == TimingRole::latchDtoQ()) {
Vertex *out_vertex = edge->to(graph_);
LockGuard lock(pending_latch_outputs_lock_);
pending_latch_outputs_.insert(out_vertex);
LockGuard lock(postponed_arrivals_lock_);
postponed_arrivals_.insert(out_vertex);
}
}
}
void
Search::enqueueLatchOutput(Vertex *vertex)
Search::postponeArrivals(Vertex *vertex)
{
LockGuard lock(pending_latch_outputs_lock_);
pending_latch_outputs_.insert(vertex);
LockGuard lock(postponed_arrivals_lock_);
postponed_arrivals_.insert(vertex);
}
void
@ -1995,7 +2007,8 @@ PathVisitor::visitEdge(const Pin *from_pin,
Path *from_path = from_iter.next();
const Mode *mode = from_path->mode(this);
if (mode == prev_mode
|| (pred_->searchFrom(from_vertex, mode) && pred_->searchThru(edge, mode)
|| (pred_->searchFrom(from_vertex, mode)
&& pred_->searchThru(edge, mode)
&& pred_->searchTo(to_vertex, mode))) {
prev_mode = mode;
const MinMax *min_max = from_path->minMax(this);