1 // OpenSTA, Static Timing Analyzer
2 // Copyright (c) 2021, Parallax Software, Inc.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
16 
17 #include "VisitPathEnds.hh"
18 
19 #include "Debug.hh"
20 #include "Liberty.hh"
21 #include "Network.hh"
22 #include "TimingArc.hh"
23 #include "ExceptionPath.hh"
24 #include "PortDelay.hh"
25 #include "Sdc.hh"
26 #include "Graph.hh"
27 #include "ClkInfo.hh"
28 #include "Tag.hh"
29 #include "PathVertex.hh"
30 #include "PathAnalysisPt.hh"
31 #include "PathEnd.hh"
32 #include "Search.hh"
33 #include "GatedClk.hh"
34 
35 namespace sta {
36 
VisitPathEnds(const StaState * sta)37 VisitPathEnds::VisitPathEnds(const StaState *sta) :
38   StaState(sta)
39 {
40 }
41 
42 void
visitPathEnds(Vertex * vertex,PathEndVisitor * visitor)43 VisitPathEnds::visitPathEnds(Vertex *vertex,
44 			     PathEndVisitor *visitor)
45 {
46   visitPathEnds(vertex, nullptr, MinMaxAll::all(), false, visitor);
47 }
48 
49 void
visitPathEnds(Vertex * vertex,const Corner * corner,const MinMaxAll * min_max,bool filtered,PathEndVisitor * visitor)50 VisitPathEnds::visitPathEnds(Vertex *vertex,
51 			     const Corner *corner,
52 			     const MinMaxAll *min_max,
53 			     bool filtered,
54 			     PathEndVisitor *visitor)
55 {
56   // Ignore slack on bidirect driver vertex.  The load vertex gets the slack.
57   if (!vertex->isBidirectDriver()) {
58     const Pin *pin = vertex->pin();
59     debugPrint(debug_, "search", 2, "find end slack %s",
60                vertex->name(sdc_network_));
61     visitor->vertexBegin(vertex);
62     bool is_constrained = false;
63     visitClkedPathEnds(pin, vertex, corner, min_max, filtered, visitor,
64 		       is_constrained);
65     if (search_->unconstrainedPaths()
66 	&& !is_constrained
67 	&& !vertex->isDisabledConstraint())
68       visitUnconstrainedPathEnds(pin, vertex, corner, min_max, filtered,
69 				 visitor);
70     visitor->vertexEnd(vertex);
71   }
72 }
73 
74 void
visitClkedPathEnds(const Pin * pin,Vertex * vertex,const Corner * corner,const MinMaxAll * min_max,bool filtered,PathEndVisitor * visitor,bool & is_constrained)75 VisitPathEnds::visitClkedPathEnds(const Pin *pin,
76 				  Vertex *vertex,
77 				  const Corner *corner,
78 				  const MinMaxAll *min_max,
79 				  bool filtered,
80 				  PathEndVisitor *visitor,
81 				  bool &is_constrained)
82 {
83   bool is_segment_start = search_->isSegmentStart(pin);
84   VertexPathIterator path_iter(vertex, this);
85   while (path_iter.hasNext()) {
86     PathVertex *path = path_iter.next();
87     PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
88     const MinMax *path_min_max = path_ap->pathMinMax();
89     const RiseFall *end_rf = path->transition(this);
90     Tag *tag = path->tag(this);
91     if ((corner == nullptr
92 	 || path_ap->corner() == corner)
93 	&& min_max->matches(path_min_max)
94 	// Ignore generated clock source paths.
95 	&& !path->clkInfo(this)->isGenClkSrcPath()
96 	&& !falsePathTo(path, pin, end_rf, path_min_max)
97 	// Ignore segment startpoint paths.
98 	&& (!is_segment_start
99 	    || !tag->isSegmentStart())) {
100       // set_output_delay to timing check has precidence.
101       if (sdc_->hasOutputDelay(pin))
102 	visitOutputDelayEnd(pin, path, end_rf, path_ap, filtered, visitor,
103 			    is_constrained);
104       else if (vertex->hasChecks())
105 	visitCheckEnd(pin, vertex, path, end_rf, path_ap, filtered, visitor,
106 		      is_constrained);
107       else if (!sdc_->exceptionToInvalid(pin)
108 	       && (!filtered
109 		   || search_->matchesFilter(path, nullptr))) {
110 	PathDelay *path_delay = pathDelayTo(path, pin, end_rf, path_min_max);
111 	if (path_delay) {
112 	  PathEndPathDelay path_end(path_delay, path, this);
113 	  visitor->visit(&path_end);
114 	  is_constrained = true;
115 	}
116       }
117       if (sdc_->gatedClkChecksEnabled())
118 	visitGatedClkEnd(pin, vertex, path, end_rf, path_ap, filtered, visitor,
119 			 is_constrained);
120       visitDataCheckEnd(pin, path, end_rf, path_ap, filtered, visitor,
121 			is_constrained);
122     }
123   }
124 }
125 
126 void
visitCheckEnd(const Pin * pin,Vertex * vertex,Path * path,const RiseFall * end_rf,const PathAnalysisPt * path_ap,bool filtered,PathEndVisitor * visitor,bool & is_constrained)127 VisitPathEnds::visitCheckEnd(const Pin *pin,
128 			     Vertex *vertex,
129 			     Path *path,
130 			     const RiseFall *end_rf,
131 			     const PathAnalysisPt *path_ap,
132 			     bool filtered,
133 			     PathEndVisitor *visitor,
134 			     bool &is_constrained)
135 {
136   ClockEdge *src_clk_edge = path->clkEdge(this);
137   Clock *src_clk = path->clock(this);
138   const MinMax *min_max = path_ap->pathMinMax();
139   const PathAnalysisPt *tgt_clk_path_ap = path_ap->tgtClkAnalysisPt();
140   bool check_clked = false;
141   VertexInEdgeIterator edge_iter(vertex, graph_);
142   while (edge_iter.hasNext()) {
143     Edge *edge = edge_iter.next();
144     Vertex *tgt_clk_vertex = edge->from(graph_);
145     const TimingRole *check_role = edge->role();
146     if (checkEdgeEnabled(edge)
147 	&& check_role->pathMinMax() == min_max) {
148       TimingArcSet *arc_set = edge->timingArcSet();
149       TimingArcSetArcIterator arc_iter(arc_set);
150       while (arc_iter.hasNext()) {
151 	TimingArc *check_arc = arc_iter.next();
152 	RiseFall *clk_rf = check_arc->fromTrans()->asRiseFall();
153 	if (check_arc->toTrans()->asRiseFall() == end_rf
154 	    && clk_rf) {
155 	  VertexPathIterator tgt_clk_path_iter(tgt_clk_vertex, clk_rf,
156 					       tgt_clk_path_ap, this);
157 	  while (tgt_clk_path_iter.hasNext()) {
158 	    PathVertex *tgt_clk_path = tgt_clk_path_iter.next();
159 	    ClkInfo *tgt_clk_info = tgt_clk_path->clkInfo(this);
160 	    const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this);
161 	    const Clock *tgt_clk = tgt_clk_path->clock(this);
162 	    const Pin *tgt_pin = tgt_clk_vertex->pin();
163 	    ExceptionPath *exception = exceptionTo(path, pin, end_rf,
164 						   tgt_clk_edge, min_max);
165 	    // Ignore generated clock source paths.
166 	    if (!tgt_clk_info->isGenClkSrcPath()
167 		&& !search_->pathPropagatedToClkSrc(tgt_pin, tgt_clk_path)) {
168 	      if (tgt_clk_path->isClock(this)) {
169 		check_clked = true;
170 		if (!filtered
171 		    || search_->matchesFilter(path, tgt_clk_edge)) {
172 		  if (src_clk_edge
173 		      && tgt_clk != sdc_->defaultArrivalClock()
174 		      && sdc_->sameClockGroup(src_clk, tgt_clk)
175 		      && !sdc_->clkStopPropagation(tgt_pin, tgt_clk)
176 		      && (search_->checkDefaultArrivalPaths()
177 			  || src_clk_edge
178 			  != sdc_->defaultArrivalClockEdge())
179 		      // False paths and path delays override
180 		      // paths.
181 		      && (exception == nullptr
182 			  || exception->isFilter()
183 			  || exception->isGroupPath()
184 			  || exception->isMultiCycle())) {
185 		    MultiCyclePath *mcp=dynamic_cast<MultiCyclePath*>(exception);
186 		    if (network_->isLatchData(pin)
187 			&& check_role == TimingRole::setup()) {
188 		      PathEndLatchCheck path_end(path, check_arc, edge,
189 						 tgt_clk_path, mcp, nullptr,
190 						 this);
191 		      visitor->visit(&path_end);
192 		      is_constrained = true;
193 		    }
194 		    else {
195 		      PathEndCheck path_end(path, check_arc, edge,
196 					    tgt_clk_path, mcp, this);
197 		      visitor->visit(&path_end);
198 		      is_constrained = true;
199 		    }
200 		  }
201 		  else if (exception
202 			   && exception->isPathDelay()
203 			   && (src_clk == nullptr
204 			       || sdc_->sameClockGroup(src_clk,
205 							       tgt_clk))) {
206 		    PathDelay *path_delay = dynamic_cast<PathDelay*>(exception);
207 		    if (network_->isLatchData(pin)
208 			&& check_role == TimingRole::setup()) {
209 		      PathEndLatchCheck path_end(path, check_arc, edge,
210 						 tgt_clk_path, nullptr,
211 						 path_delay, this);
212 		      visitor->visit(&path_end);
213 		    }
214 		    else {
215 		      PathEndPathDelay path_end(path_delay, path, tgt_clk_path,
216 						check_arc, edge, this);
217 		      visitor->visit(&path_end);
218 		      is_constrained = true;
219 		    }
220 		  }
221 		}
222 	      }
223 	    }
224 	  }
225 	}
226       }
227     }
228   }
229   if (!check_clked
230       && !sdc_->exceptionToInvalid(pin))
231     visitCheckEndUnclked(pin, vertex, path, end_rf, path_ap, filtered,
232 			 visitor, is_constrained);
233 }
234 
235 void
visitCheckEndUnclked(const Pin * pin,Vertex * vertex,Path * path,const RiseFall * end_rf,const PathAnalysisPt * path_ap,bool filtered,PathEndVisitor * visitor,bool & is_constrained)236 VisitPathEnds::visitCheckEndUnclked(const Pin *pin,
237 				    Vertex *vertex,
238 				    Path *path,
239 				    const RiseFall *end_rf,
240 				    const PathAnalysisPt *path_ap,
241 				    bool filtered,
242 				    PathEndVisitor *visitor,
243 				    bool &is_constrained)
244 {
245   const MinMax *min_max = path_ap->pathMinMax();
246   VertexInEdgeIterator edge_iter(vertex, graph_);
247   while (edge_iter.hasNext()) {
248     Edge *edge = edge_iter.next();
249     const TimingRole *check_role = edge->role();
250     if (checkEdgeEnabled(edge)
251 	&& check_role->pathMinMax() == min_max) {
252       TimingArcSet *arc_set = edge->timingArcSet();
253       TimingArcSetArcIterator arc_iter(arc_set);
254       while (arc_iter.hasNext()) {
255 	TimingArc *check_arc = arc_iter.next();
256 	RiseFall *clk_rf = check_arc->fromTrans()->asRiseFall();
257 	if (check_arc->toTrans()->asRiseFall() == end_rf
258 	    && clk_rf
259 	    && (!filtered
260 		|| search_->matchesFilter(path, nullptr))) {
261 	  ExceptionPath *exception = exceptionTo(path, pin, end_rf,
262 						 nullptr, min_max);
263 	  // False paths and path delays override multicycle paths.
264 	  if (exception
265 	      && exception->isPathDelay()) {
266 	    PathDelay *path_delay = dynamic_cast<PathDelay*>(exception);
267 	    PathEndPathDelay path_end(path_delay, path, nullptr,
268 				      check_arc, edge, this);
269 	    visitor->visit(&path_end);
270 	    is_constrained = true;
271 	  }
272 	}
273       }
274     }
275   }
276 }
277 
278 bool
checkEdgeEnabled(Edge * edge) const279 VisitPathEnds::checkEdgeEnabled(Edge *edge) const
280 {
281   const TimingRole *check_role = edge->role();
282   return check_role->isTimingCheck()
283     && search_->evalPred()->searchFrom(edge->from(graph_))
284     && !edge->isDisabledConstraint()
285     && !edge->isDisabledCond()
286     && !sdc_->isDisabledCondDefault(edge)
287     && !((check_role == TimingRole::recovery()
288 	  || check_role == TimingRole::removal())
289 	 && !sdc_->recoveryRemovalChecksEnabled());
290 }
291 
292 void
visitOutputDelayEnd(const Pin * pin,Path * path,const RiseFall * end_rf,const PathAnalysisPt * path_ap,bool filtered,PathEndVisitor * visitor,bool & is_constrained)293 VisitPathEnds::visitOutputDelayEnd(const Pin *pin,
294 				   Path *path,
295 				   const RiseFall *end_rf,
296 				   const PathAnalysisPt *path_ap,
297 				   bool filtered,
298 				   PathEndVisitor *visitor,
299 				   bool &is_constrained)
300 {
301   const MinMax *min_max = path_ap->pathMinMax();
302   OutputDelaySet *output_delays = sdc_->outputDelaysLeafPin(pin);
303   if (output_delays) {
304     for (OutputDelay *output_delay : *output_delays) {
305       float margin;
306       bool exists;
307       output_delay->delays()->value(end_rf, min_max, margin, exists);
308       if (exists) {
309 	const Pin *ref_pin = output_delay->refPin();
310 	ClockEdge *tgt_clk_edge = output_delay->clkEdge();
311 	if (!filtered
312 	    || search_->matchesFilter(path, tgt_clk_edge)) {
313 	  if (ref_pin) {
314 	    Clock *tgt_clk = output_delay->clock();
315 	    Vertex *ref_vertex = graph_->pinLoadVertex(ref_pin);
316 	    RiseFall *ref_rf = output_delay->refTransition();
317 	    VertexPathIterator ref_path_iter(ref_vertex,ref_rf,path_ap,this);
318 	    while (ref_path_iter.hasNext()) {
319 	      PathVertex *ref_path = ref_path_iter.next();
320 	      if (ref_path->isClock(this)
321 		  && (tgt_clk == nullptr
322 		      || ref_path->clock(this) == tgt_clk))
323 		visitOutputDelayEnd1(output_delay, pin, path, end_rf,
324 				     ref_path->clkEdge(this), ref_path, min_max,
325 				     visitor, is_constrained);
326 	    }
327 	  }
328 	  else
329 	    visitOutputDelayEnd1(output_delay, pin, path, end_rf,
330 				 tgt_clk_edge, nullptr, min_max,
331 				 visitor, is_constrained);
332 	}
333       }
334     }
335   }
336 }
337 
338 void
visitOutputDelayEnd1(OutputDelay * output_delay,const Pin * pin,Path * path,const RiseFall * end_rf,const ClockEdge * tgt_clk_edge,PathVertex * ref_path,const MinMax * min_max,PathEndVisitor * visitor,bool & is_constrained)339 VisitPathEnds::visitOutputDelayEnd1(OutputDelay *output_delay,
340 				    const Pin *pin,
341 				    Path *path,
342 				    const RiseFall *end_rf,
343 				    const ClockEdge *tgt_clk_edge,
344 				    PathVertex *ref_path,
345 				    const MinMax *min_max,
346 				    PathEndVisitor *visitor,
347 				    bool &is_constrained)
348 {
349   // Target clk is not required for path delay,
350   // but the exception may be -to clk.
351   ExceptionPath *exception = exceptionTo(path, pin, end_rf, tgt_clk_edge,
352 					 min_max);
353   if (exception
354       && exception->isPathDelay()) {
355     PathDelay *path_delay = dynamic_cast<PathDelay*>(exception);
356     PathEndPathDelay path_end(path_delay, path, output_delay, this);
357     visitor->visit(&path_end);
358     is_constrained = true;
359   }
360   else if (tgt_clk_edge
361 	   && sdc_->sameClockGroup(path->clock(this), tgt_clk_edge->clock())
362 	   // False paths and path delays override.
363 	   && (exception == nullptr
364 	       || exception->isFilter()
365 	       || exception->isGroupPath()
366 	       || exception->isMultiCycle())) {
367     MultiCyclePath *mcp = dynamic_cast<MultiCyclePath*>(exception);
368     PathEndOutputDelay path_end(output_delay, path, ref_path, mcp, this);
369     visitor->visit(&path_end);
370     is_constrained = true;
371   }
372 }
373 
374 ////////////////////////////////////////////////////////////////
375 
376 // Look for clock gating functions where path is the clock enable.
377 void
visitGatedClkEnd(const Pin * pin,Vertex * vertex,Path * path,const RiseFall * end_rf,const PathAnalysisPt * path_ap,bool filtered,PathEndVisitor * visitor,bool & is_constrained)378 VisitPathEnds::visitGatedClkEnd(const Pin *pin,
379 				Vertex *vertex,
380 				Path *path,
381 				const RiseFall *end_rf,
382 				const PathAnalysisPt *path_ap,
383 				bool filtered,
384 				PathEndVisitor *visitor,
385 				bool &is_constrained)
386 {
387   ClockEdge *src_clk_edge = path->clkEdge(this);
388   if (src_clk_edge) {
389     GatedClk *gated_clk = search_->gatedClk();
390     Clock *src_clk = src_clk_edge->clock();
391     bool is_gated_clk_enable;
392     const Pin *clk_pin;
393     LogicValue logic_active_value;
394     gated_clk->isGatedClkEnable(vertex,
395 				is_gated_clk_enable, clk_pin, logic_active_value);
396     if (is_gated_clk_enable) {
397       const PathAnalysisPt *clk_path_ap = path_ap->tgtClkAnalysisPt();
398       const MinMax *min_max = path_ap->pathMinMax();
399       Vertex *clk_vertex = graph_->pinLoadVertex(clk_pin);
400       LogicValue active_value =
401 	sdc_->clockGatingActiveValue(clk_pin, pin);
402       RiseFall *clk_rf =
403 	// Clock active value specified by set_clock_gating_check
404 	// overrides the library cell function active value.
405 	gated_clk->gatedClkActiveTrans((active_value == LogicValue::unknown) ?
406 				       logic_active_value : active_value,
407 				       min_max);
408       VertexPathIterator clk_path_iter(clk_vertex, clk_rf, clk_path_ap, this);
409       while (clk_path_iter.hasNext()) {
410 	PathVertex *clk_path = clk_path_iter.next();
411 	const ClockEdge *clk_edge = clk_path->clkEdge(this);
412 	const Clock *clk = clk_edge ? clk_edge->clock() : nullptr;
413 	if (clk_path->isClock(this)
414 	    // Ignore unclocked paths (from path delay constraints).
415 	    && clk_edge
416 	    && clk_edge != sdc_->defaultArrivalClockEdge()
417 	    // Ignore generated clock source paths.
418 	    && !path->clkInfo(this)->isGenClkSrcPath()
419 	    && !search_->pathPropagatedToClkSrc(clk_pin, clk_path)
420 	    && !sdc_->clkStopPropagation(pin, clk)
421 	    && clk_vertex->hasDownstreamClkPin()) {
422 	  TimingRole *check_role = (min_max == MinMax::max())
423 	    ? TimingRole::gatedClockSetup()
424 	    : TimingRole::gatedClockHold();
425 	  float margin = clockGatingMargin(clk, clk_pin,
426 					   pin, end_rf, min_max);
427 	  ExceptionPath *exception = exceptionTo(path, pin, end_rf,
428 						 clk_edge, min_max);
429 	  if (sdc_->sameClockGroup(src_clk, clk)
430 	      // False paths and path delays override.
431 	      && (exception == nullptr
432 		  || exception->isFilter()
433 		  || exception->isGroupPath()
434 		  || exception->isMultiCycle())
435 	      && (!filtered
436 		  || search_->matchesFilter(path, clk_edge))) {
437 	    MultiCyclePath *mcp =
438 	      dynamic_cast<MultiCyclePath *>(exception);
439 	    PathEndGatedClock path_end(path, clk_path, check_role,
440 				       mcp, margin, this);
441 	    visitor->visit(&path_end);
442 	    is_constrained = true;
443 	  }
444 	}
445       }
446     }
447   }
448 }
449 
450 // Gated clock setup/hold margin respecting precedence rules.
451 // Look for margin from highest precedence level to lowest.
452 float
clockGatingMargin(const Clock * clk,const Pin * clk_pin,const Pin * enable_pin,const RiseFall * enable_rf,const SetupHold * setup_hold)453 VisitPathEnds::clockGatingMargin(const Clock *clk,
454 				 const Pin *clk_pin,
455 				 const Pin *enable_pin,
456 				 const RiseFall *enable_rf,
457 				 const SetupHold *setup_hold)
458 {
459   bool exists;
460   float margin;
461   sdc_->clockGatingMarginEnablePin(enable_pin, enable_rf,
462 					   setup_hold, exists, margin);
463   if (exists)
464     return margin;
465   Instance *inst = network_->instance(enable_pin);
466   sdc_->clockGatingMarginInstance(inst, enable_rf, setup_hold,
467 					  exists, margin);
468   if (exists)
469     return margin;
470   sdc_->clockGatingMarginClkPin(clk_pin, enable_rf, setup_hold,
471 					exists, margin);
472   if (exists)
473     return margin;
474   sdc_->clockGatingMarginClk(clk, enable_rf, setup_hold,
475 				     exists, margin);
476   if (exists)
477     return margin;
478   sdc_->clockGatingMargin(enable_rf, setup_hold,
479 				  exists, margin);
480   if (exists)
481     return margin;
482   else
483     return 0.0;
484 }
485 
486 ////////////////////////////////////////////////////////////////
487 
488 void
visitDataCheckEnd(const Pin * pin,Path * path,const RiseFall * end_rf,const PathAnalysisPt * path_ap,bool filtered,PathEndVisitor * visitor,bool & is_constrained)489 VisitPathEnds::visitDataCheckEnd(const Pin *pin,
490 				 Path *path,
491 				 const RiseFall *end_rf,
492 				 const PathAnalysisPt *path_ap,
493 				 bool filtered,
494 				 PathEndVisitor *visitor,
495 				 bool &is_constrained)
496 {
497   ClockEdge *src_clk_edge = path->clkEdge(this);
498   if (src_clk_edge) {
499     DataCheckSet *checks = sdc_->dataChecksTo(pin);
500     if (checks) {
501       const Clock *src_clk = src_clk_edge->clock();
502       const MinMax *min_max = path_ap->pathMinMax();
503       const PathAnalysisPt *clk_ap = path_ap->tgtClkAnalysisPt();
504       DataCheckSet::Iterator check_iter(checks);
505       while (check_iter.hasNext()) {
506 	DataCheck *check = check_iter.next();
507  	const Pin *from_pin = check->from();
508  	Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
509 	for (auto from_rf : RiseFall::range()) {
510  	  float margin;
511  	  bool margin_exists;
512  	  check->margin(from_rf, end_rf, min_max, margin, margin_exists);
513  	  if (margin_exists)
514 	    visitDataCheckEnd1(check, pin, path, src_clk, end_rf,
515 			       min_max, clk_ap, from_pin, from_vertex,
516 			       from_rf, filtered, visitor, is_constrained);
517 	}
518       }
519     }
520   }
521 }
522 
523 bool
visitDataCheckEnd1(DataCheck * check,const Pin * pin,Path * path,const Clock * src_clk,const RiseFall * end_rf,const MinMax * min_max,const PathAnalysisPt * clk_ap,const Pin * from_pin,Vertex * from_vertex,RiseFall * from_rf,bool filtered,PathEndVisitor * visitor,bool & is_constrained)524 VisitPathEnds::visitDataCheckEnd1(DataCheck *check,
525 				  const Pin *pin,
526 				  Path *path,
527 				  const Clock *src_clk,
528 				  const RiseFall *end_rf,
529 				  const MinMax *min_max,
530 				  const PathAnalysisPt *clk_ap,
531 				  const Pin *from_pin,
532 				  Vertex *from_vertex,
533 				  RiseFall *from_rf,
534 				  bool filtered,
535 				  PathEndVisitor *visitor,
536 				  bool &is_constrained)
537 {
538   bool found_from_path = false;
539   VertexPathIterator tgt_clk_path_iter(from_vertex,from_rf,clk_ap,this);
540   while (tgt_clk_path_iter.hasNext()) {
541     PathVertex *tgt_clk_path = tgt_clk_path_iter.next();
542     const ClockEdge *tgt_clk_edge = tgt_clk_path->clkEdge(this);
543     const Clock *tgt_clk = tgt_clk_edge ? tgt_clk_edge->clock() : nullptr;
544     ExceptionPath *exception = exceptionTo(path, pin, end_rf,
545 					   tgt_clk_edge, min_max);
546     // Ignore generated clock source paths.
547     if (!tgt_clk_path->clkInfo(this)->isGenClkSrcPath()
548 	&& !search_->pathPropagatedToClkSrc(from_pin, tgt_clk_path)) {
549       found_from_path = true;
550       if (sdc_->sameClockGroup(src_clk, tgt_clk)
551 	  && !sdc_->clkStopPropagation(from_pin, tgt_clk)
552 	  // False paths and path delays override.
553 	  && (exception == 0
554 	      || exception->isFilter()
555 	      || exception->isGroupPath()
556 	      || exception->isMultiCycle())
557 	  && (!filtered
558 	      || search_->matchesFilter(path, tgt_clk_edge))) {
559 	MultiCyclePath *mcp=dynamic_cast<MultiCyclePath*>(exception);
560 	PathEndDataCheck path_end(check, path, tgt_clk_path, mcp, this);
561 	visitor->visit(&path_end);
562 	is_constrained = true;
563       }
564     }
565   }
566   return found_from_path;
567 }
568 
569 ////////////////////////////////////////////////////////////////
570 
571 void
visitUnconstrainedPathEnds(const Pin * pin,Vertex * vertex,const Corner * corner,const MinMaxAll * min_max,bool filtered,PathEndVisitor * visitor)572 VisitPathEnds::visitUnconstrainedPathEnds(const Pin *pin,
573 					  Vertex *vertex,
574 					  const Corner *corner,
575 					  const MinMaxAll *min_max,
576 					  bool filtered,
577 					  PathEndVisitor *visitor)
578 {
579   VertexPathIterator path_iter(vertex, this);
580   while (path_iter.hasNext()) {
581     PathVertex *path = path_iter.next();
582     PathAnalysisPt *path_ap = path->pathAnalysisPt(this);
583     const MinMax *path_min_max = path_ap->pathMinMax();
584     if ((corner == nullptr
585 	 || path_ap->corner() == corner)
586 	&& min_max->matches(path_min_max)
587 	// Ignore generated clock source paths.
588 	&& !path->clkInfo(this)->isGenClkSrcPath()
589  	&& !search_->pathPropagatedToClkSrc(pin, path)
590 	&& (!filtered
591 	    || search_->matchesFilter(path, nullptr))
592 	&& !falsePathTo(path, pin, path->transition(this),
593 			path->minMax(this))) {
594       PathEndUnconstrained path_end(path);
595       visitor->visit(&path_end);
596     }
597   }
598 }
599 
600 ////////////////////////////////////////////////////////////////
601 
602 bool
falsePathTo(Path * path,const Pin * pin,const RiseFall * rf,const MinMax * min_max)603 VisitPathEnds::falsePathTo(Path *path,
604 			   const Pin *pin,
605 			   const RiseFall *rf,
606 			   const MinMax *min_max)
607 {
608   ExceptionPath *exception = search_->exceptionTo(ExceptionPathType::false_path, path,
609 						  pin, rf, nullptr, min_max,
610 						  false, false);
611   return exception != nullptr;
612 }
613 
614 PathDelay *
pathDelayTo(Path * path,const Pin * pin,const RiseFall * rf,const MinMax * min_max)615 VisitPathEnds::pathDelayTo(Path *path,
616 			   const Pin *pin,
617 			   const RiseFall *rf,
618 			   const MinMax *min_max)
619 {
620   ExceptionPath *exception = search_->exceptionTo(ExceptionPathType::path_delay,
621 						  path, pin, rf, nullptr,
622 						  min_max, false,
623 						  // Register clk pins only
624 						  // match with -to pin.
625 						  network_->isRegClkPin(pin));
626   return dynamic_cast<PathDelay*>(exception);
627 }
628 
629 ExceptionPath *
exceptionTo(const Path * path,const Pin * pin,const RiseFall * rf,const ClockEdge * clk_edge,const MinMax * min_max) const630 VisitPathEnds::exceptionTo(const Path *path,
631 			   const Pin *pin,
632 			   const RiseFall *rf,
633 			   const ClockEdge *clk_edge,
634 			   const MinMax *min_max) const
635 {
636   return search_->exceptionTo(ExceptionPathType::any, path, pin, rf, clk_edge,
637 			      min_max, false, false);
638 }
639 
640 } // namespace
641