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