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 "CheckFanoutLimits.hh"
18 
19 #include "Fuzzy.hh"
20 #include "Liberty.hh"
21 #include "Network.hh"
22 #include "Sdc.hh"
23 #include "Sim.hh"
24 #include "PortDirection.hh"
25 #include "Graph.hh"
26 #include "Search.hh"
27 
28 namespace sta {
29 
30 class PinFanoutLimitSlackLess
31 {
32 public:
33   PinFanoutLimitSlackLess(const MinMax *min_max,
34 			  CheckFanoutLimits *check_fanout_limit,
35 			  const StaState *sta);
36   bool operator()(Pin *pin1,
37 		  Pin *pin2) const;
38 
39 private:
40   const MinMax *min_max_;
41   CheckFanoutLimits *check_fanout_limit_;
42   const StaState *sta_;
43 
44 };
45 
PinFanoutLimitSlackLess(const MinMax * min_max,CheckFanoutLimits * check_fanout_limit,const StaState * sta)46 PinFanoutLimitSlackLess::PinFanoutLimitSlackLess(const MinMax *min_max,
47 						 CheckFanoutLimits *check_fanout_limit,
48 						 const StaState *sta) :
49   min_max_(min_max),
50   check_fanout_limit_(check_fanout_limit),
51   sta_(sta)
52 {
53 }
54 
55 bool
operator ()(Pin * pin1,Pin * pin2) const56 PinFanoutLimitSlackLess::operator()(Pin *pin1,
57 				    Pin *pin2) const
58 {
59   float fanout1, fanout2;
60   float limit1, limit2, slack1, slack2;
61   check_fanout_limit_->checkFanout(pin1, min_max_,
62 				   fanout1, limit1, slack1);
63   check_fanout_limit_->checkFanout(pin2, min_max_,
64 				   fanout2, limit2, slack2);
65   return fuzzyLess(slack1, slack2)
66     || (fuzzyEqual(slack1, slack2)
67 	// Break ties for the sake of regression stability.
68 	&& sta_->network()->pinLess(pin1, pin2));
69 }
70 
71 ////////////////////////////////////////////////////////////////
72 
CheckFanoutLimits(const Sta * sta)73 CheckFanoutLimits::CheckFanoutLimits(const Sta *sta) :
74   sta_(sta)
75 {
76 }
77 
78 void
checkFanout(const Pin * pin,const MinMax * min_max,float & fanout,float & limit,float & slack) const79 CheckFanoutLimits::checkFanout(const Pin *pin,
80 			       const MinMax *min_max,
81 			       // Return values.
82 			       float &fanout,
83 			       float &limit,
84 			       float &slack) const
85 {
86   fanout = 0.0;
87   limit = 0.0;
88   slack = MinMax::min()->initValue();
89 
90   float limit1;
91   bool limit1_exists;
92   findLimit(pin, min_max, limit1, limit1_exists);
93   if (limit1_exists)
94     checkFanout(pin, min_max, limit1,
95 		fanout, slack, limit);
96 }
97 
98 // return the tightest limit.
99 void
findLimit(const Pin * pin,const MinMax * min_max,float & limit,bool & exists) const100 CheckFanoutLimits::findLimit(const Pin *pin,
101 			     const MinMax *min_max,
102 			     // Return values.
103 			     float &limit,
104 			     bool &exists) const
105 {
106   const Network *network = sta_->network();
107   Sdc *sdc = sta_->sdc();
108 
109   // Default to top ("design") limit.
110   Cell *top_cell = network->cell(network->topInstance());
111   sdc->fanoutLimit(top_cell, min_max,
112 		   limit, exists);
113 
114   float limit1;
115   bool exists1;
116   if (network->isTopLevelPort(pin)) {
117     Port *port = network->port(pin);
118     sdc->fanoutLimit(port, min_max, limit1, exists1);
119     if (exists1
120 	&& (!exists
121 	    || min_max->compare(limit, limit1))) {
122       limit = limit1;
123       exists = true;
124     }
125   }
126   else {
127     Cell *cell = network->cell(network->instance(pin));
128     sdc->fanoutLimit(cell, min_max,
129 		     limit1, exists1);
130     if (exists1
131 	&& (!exists
132 	    || min_max->compare(limit, limit1))) {
133       limit = limit1;
134       exists = true;
135     }
136     LibertyPort *port = network->libertyPort(pin);
137     if (port) {
138       port->fanoutLimit(min_max, limit1, exists1);
139       if (!exists1
140 	  && min_max == MinMax::max()
141 	  && port->direction()->isAnyOutput())
142 	port->libertyLibrary()->defaultMaxFanout(limit1, exists1);
143       if (exists1
144 	  && (!exists
145 	      || min_max->compare(limit, limit1))) {
146 	limit = limit1;
147 	exists = true;
148       }
149     }
150   }
151 }
152 
153 void
checkFanout(const Pin * pin,const MinMax * min_max,float limit1,float & fanout,float & slack,float & limit) const154 CheckFanoutLimits::checkFanout(const Pin *pin,
155 			       const MinMax *min_max,
156 			       float limit1,
157 			       // Return values.
158 			       float &fanout,
159 			       float &slack,
160 			       float &limit) const
161 {
162   float fanout1 = fanoutLoad(pin);
163   float slack1 = (min_max == MinMax::max())
164     ? limit1 - fanout1
165     : fanout1 - limit1;
166   if (fuzzyLessEqual(slack1, slack)) {
167     fanout = fanout1;
168     slack = slack1;
169     limit = limit1;
170   }
171 }
172 
173 float
fanoutLoad(const Pin * pin) const174 CheckFanoutLimits::fanoutLoad(const Pin *pin) const
175 {
176   float fanout = 0;
177   const Network *network = sta_->network();
178   Net *net = network->net(pin);
179   if (net) {
180     NetPinIterator *pin_iter = network->pinIterator(net);
181     while (pin_iter->hasNext()) {
182       Pin *pin = pin_iter->next();
183       if (network->isLoad(pin)) {
184 	LibertyPort *port = network->libertyPort(pin);
185 	if (port) {
186 	  float fanout_load;
187 	  bool exists;
188 	  port->fanoutLoad(fanout_load, exists);
189 	  if (!exists) {
190 	    LibertyLibrary *lib = port->libertyLibrary();
191 	    lib->defaultFanoutLoad(fanout_load, exists);
192 	  }
193 	  if (exists)
194 	    fanout += fanout_load;
195 	}
196 	else
197 	  fanout += 1;
198       }
199     }
200     delete pin_iter;
201   }
202   return fanout;
203 }
204 
205 ////////////////////////////////////////////////////////////////
206 
207 PinSeq *
checkFanoutLimits(Net * net,bool violators,const MinMax * min_max)208 CheckFanoutLimits::checkFanoutLimits(Net *net,
209                                      bool violators,
210                                      const MinMax *min_max)
211 {
212   const Network *network = sta_->network();
213   PinSeq *fanout_pins = new PinSeq;
214   float min_slack = MinMax::min()->initValue();
215   if (net) {
216     NetPinIterator *pin_iter = network->pinIterator(net);
217     while (pin_iter->hasNext()) {
218       Pin *pin = pin_iter->next();
219       checkFanoutLimits(pin, violators, min_max, fanout_pins, min_slack);
220     }
221     delete pin_iter;
222   }
223   else {
224     LeafInstanceIterator *inst_iter = network->leafInstanceIterator();
225     while (inst_iter->hasNext()) {
226       Instance *inst = inst_iter->next();
227       checkFanoutLimits(inst, violators, min_max, fanout_pins, min_slack);
228     }
229     delete inst_iter;
230     // Check top level ports.
231     checkFanoutLimits(network->topInstance(), violators, min_max,
232                     fanout_pins, min_slack);
233   }
234   sort(fanout_pins, PinFanoutLimitSlackLess(min_max, this, sta_));
235   // Keep the min slack pin unless all violators or net pins.
236   if (!fanout_pins->empty() && !violators && net == nullptr)
237     fanout_pins->resize(1);
238   return fanout_pins;
239 }
240 
241 void
checkFanoutLimits(Instance * inst,bool violators,const MinMax * min_max,PinSeq * fanout_pins,float & min_slack)242 CheckFanoutLimits::checkFanoutLimits(Instance *inst,
243                                      bool violators,
244                                      const MinMax *min_max,
245                                      PinSeq *fanout_pins,
246                                      float &min_slack)
247 {
248   const Network *network = sta_->network();
249   InstancePinIterator *pin_iter = network->pinIterator(inst);
250   while (pin_iter->hasNext()) {
251     Pin *pin = pin_iter->next();
252     checkFanoutLimits(pin, violators, min_max, fanout_pins, min_slack);
253   }
254   delete pin_iter;
255 }
256 
257 void
checkFanoutLimits(Pin * pin,bool violators,const MinMax * min_max,PinSeq * fanout_pins,float & min_slack)258 CheckFanoutLimits::checkFanoutLimits(Pin *pin,
259                                      bool violators,
260                                      const MinMax *min_max,
261                                      PinSeq *fanout_pins,
262                                      float &min_slack)
263 {
264   if (checkPin(pin)) {
265     float fanout;
266     float limit, slack;
267     checkFanout(pin, min_max, fanout, limit, slack);
268     if (!fuzzyInf(slack)) {
269       if (violators) {
270         if (slack < 0.0)
271           fanout_pins->push_back(pin);
272       }
273       else {
274         if (fanout_pins->empty()
275             || slack < min_slack) {
276           fanout_pins->push_back(pin);
277           min_slack = slack;
278         }
279       }
280     }
281   }
282 }
283 
284 bool
checkPin(Pin * pin)285 CheckFanoutLimits::checkPin(Pin *pin)
286 {
287   const Network *network = sta_->network();
288   const Sim *sim = sta_->sim();
289   const Sdc *sdc = sta_->sdc();
290   const Graph *graph = sta_->graph();
291   Vertex *vertex = graph->pinLoadVertex(pin);
292   return network->direction(pin)->isAnyOutput()
293     && !sim->logicZeroOne(pin)
294     && !sdc->isDisabled(pin)
295     && !(vertex && sta_->isIdealClock(pin));
296 }
297 
298 } // namespace
299