1 /////////////////////////////////////////////////////////////////////////////
2 //
3 // BSD 3-Clause License
4 //
5 // Copyright (c) 2019, The Regents of the University of California
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions are met:
10 //
11 // * Redistributions of source code must retain the above copyright notice, this
12 //   list of conditions and the following disclaimer.
13 //
14 // * Redistributions in binary form must reproduce the above copyright notice,
15 //   this list of conditions and the following disclaimer in the documentation
16 //   and/or other materials provided with the distribution.
17 //
18 // * Neither the name of the copyright holder nor the names of its
19 //   contributors may be used to endorse or promote products derived from
20 //   this software without specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 // POSSIBILITY OF SUCH DAMAGE.
33 //
34 ///////////////////////////////////////////////////////////////////////////////
35 
36 #include "ppl/IOPlacer.h"
37 
38 #include <algorithm>
39 #include <random>
40 #include <sstream>
41 
42 #include "opendb/db.h"
43 #include "ord/OpenRoad.hh"
44 #include "utl/Logger.h"
45 #include "utl/algorithms.h"
46 
47 namespace ppl {
48 
49 using utl::PPL;
50 
init(odb::dbDatabase * db,Logger * logger)51 void IOPlacer::init(odb::dbDatabase* db, Logger* logger)
52 {
53   db_ = db;
54   logger_ = logger;
55   parms_ = std::make_unique<Parameters>();
56   top_grid_ = TopLayerGrid(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
57 }
58 
clear()59 void IOPlacer::clear()
60 {
61   hor_layers_.clear();
62   ver_layers_.clear();
63   top_grid_ = TopLayerGrid();
64   zero_sink_ios_.clear();
65   sections_.clear();
66   slots_.clear();
67   top_layer_slots_.clear();
68   assignment_.clear();
69   netlist_io_pins_.clear();
70   excluded_intervals_.clear();
71   netlist_.clear();
72   pin_groups_.clear();
73   *parms_ = Parameters();
74 }
75 
initNetlistAndCore(std::set<int> hor_layer_idx,std::set<int> ver_layer_idx)76 void IOPlacer::initNetlistAndCore(std::set<int> hor_layer_idx,
77                                   std::set<int> ver_layer_idx)
78 {
79   populateIOPlacer(hor_layer_idx, ver_layer_idx);
80 }
81 
initParms()82 void IOPlacer::initParms()
83 {
84   report_hpwl_ = false;
85   slots_per_section_ = 200;
86   slots_increase_factor_ = 0.01f;
87   usage_increase_factor_ = 0.01f;
88   force_pin_spread_ = true;
89   netlist_ = Netlist();
90   netlist_io_pins_ = Netlist();
91 
92   if (parms_->getReportHPWL()) {
93     report_hpwl_ = true;
94   }
95   if (parms_->getForceSpread()) {
96     force_pin_spread_ = true;
97   } else {
98     force_pin_spread_ = false;
99   }
100   if (parms_->getNumSlots() > -1) {
101     slots_per_section_ = parms_->getNumSlots();
102   }
103 
104   if (parms_->getSlotsFactor() > -1) {
105     slots_increase_factor_ = parms_->getSlotsFactor();
106   }
107   if (parms_->getUsageFactor() > -1) {
108     usage_increase_factor_ = parms_->getUsageFactor();
109   }
110 }
111 
getValidSlots(int first,int last,bool top_layer)112 std::vector<int> IOPlacer::getValidSlots(int first, int last, bool top_layer) {
113   std::vector<int> valid_slots;
114 
115   std::vector<Slot> &slots = top_layer ? top_layer_slots_ : slots_;
116 
117   for (int i = first; i <= last; i++) {
118     if (!slots[i].blocked) {
119       valid_slots.push_back(i);
120     }
121   }
122 
123   return valid_slots;
124 }
125 
randomPlacement()126 void IOPlacer::randomPlacement()
127 {
128   for (Constraint &constraint : constraints_) {
129     int first_slot = constraint.sections.front().begin_slot;
130     int last_slot = constraint.sections.back().end_slot;
131 
132     bool top_layer = constraint.interval.edge == Edge::invalid;
133     for (std::vector<int>& io_group : netlist_.getIOGroups()) {
134       const PinList& pin_list = constraint.pin_list;
135       IOPin& io_pin = netlist_.getIoPin(io_group[0]);
136       if (io_pin.isPlaced()) {
137         continue;
138       }
139 
140       if (std::find(pin_list.begin(), pin_list.end(), io_pin.getBTerm()) != pin_list.end()) {
141         std::vector<int> valid_slots = getValidSlots(first_slot, last_slot, top_layer);
142         randomPlacement(io_group, valid_slots, top_layer, true);
143       }
144     }
145 
146     std::vector<int> valid_slots = getValidSlots(first_slot, last_slot, top_layer);
147     std::vector<int> pin_indices = findPinsForConstraint(constraint, netlist_);
148     randomPlacement(pin_indices, valid_slots, top_layer, false);
149   }
150 
151   for (std::vector<int>& io_group : netlist_.getIOGroups()) {
152     IOPin& io_pin = netlist_.getIoPin(io_group[0]);
153     if (io_pin.isPlaced()) {
154       continue;
155     }
156     std::vector<int> valid_slots = getValidSlots(0, slots_.size()-1, false);
157 
158     randomPlacement(io_group, valid_slots, false, true);
159   }
160 
161   std::vector<int> valid_slots = getValidSlots(0, slots_.size()-1, false);
162 
163   std::vector<int> pin_indices;
164   for (int i = 0; i < netlist_.numIOPins(); i++) {
165     if (!netlist_.getIoPin(i).isPlaced()) {
166       pin_indices.push_back(i);
167     }
168   }
169 
170   randomPlacement(pin_indices, valid_slots, false, false);
171 }
172 
randomPlacement(std::vector<int> pin_indices,std::vector<int> slot_indices,bool top_layer,bool is_group)173 void IOPlacer::randomPlacement(std::vector<int> pin_indices, std::vector<int> slot_indices, bool top_layer, bool is_group)
174 {
175   if (pin_indices.size() > slot_indices.size()) {
176     logger_->error(PPL, 72, "Number of pins ({}) exceed number of valid positions ({})", pin_indices.size(), slot_indices.size());
177   }
178 
179   const double seed = parms_->getRandSeed();
180 
181   int num_i_os = pin_indices.size();
182   int num_slots = slot_indices.size();
183   double shift = is_group ? 1 : num_slots / double(num_i_os);
184   int idx = 0;
185   std::vector<int> vSlots(num_slots);
186   std::vector<int> vIOs(num_i_os);
187 
188   std::vector<InstancePin> instPins;
189   netlist_.getSinksOfIO(idx, instPins);
190   if (sections_.size() < 1) {
191     Section s = {Point(0, 0)};
192     sections_.push_back(s);
193   }
194 
195   std::mt19937 g;
196   g.seed(seed);
197 
198   for (size_t i = 0; i < vIOs.size(); ++i) {
199     vIOs[i] = i;
200   }
201 
202   if (vIOs.size() > 1 && !is_group) {
203     utl::shuffle(vIOs.begin(), vIOs.end(), g);
204   }
205 
206   std::vector<Slot> &slots = top_layer ? top_layer_slots_ : slots_;
207 
208   for (int pin_idx : pin_indices) {
209     int b = vIOs[0];
210     int slot_idx = slot_indices[floor(b * shift)];
211     IOPin& io_pin = netlist_.getIoPin(pin_idx);
212     io_pin.setPos(slots.at(slot_idx).pos);
213     io_pin.setPlaced();
214     slots.at(slot_idx).used = true;
215     slots.at(slot_idx).blocked = true;
216     io_pin.setLayer(slots.at(slot_idx).layer);
217     assignment_.push_back(io_pin);
218     sections_[0].net.addIONet(io_pin, instPins);
219     vIOs.erase(vIOs.begin());
220   }
221 }
222 
initIOLists()223 void IOPlacer::initIOLists()
224 {
225   int idx = 0;
226   for (IOPin& io_pin : netlist_.getIOPins()) {
227     std::vector<InstancePin> inst_pins_vector;
228     if (netlist_.numSinksOfIO(idx) != 0) {
229       netlist_.getSinksOfIO(idx, inst_pins_vector);
230       netlist_io_pins_.addIONet(io_pin, inst_pins_vector);
231     } else {
232       zero_sink_ios_.push_back(io_pin);
233       netlist_io_pins_.addIONet(io_pin, inst_pins_vector);
234     }
235     idx++;
236   }
237 
238   for (PinGroup pin_group : pin_groups_) {
239     netlist_io_pins_.createIOGroup(pin_group);
240   }
241 }
242 
checkBlocked(Edge edge,int pos)243 bool IOPlacer::checkBlocked(Edge edge, int pos)
244 {
245   for (Interval blocked_interval : excluded_intervals_) {
246     if (blocked_interval.getEdge() == edge && pos >= blocked_interval.getBegin()
247         && pos <= blocked_interval.getEnd()) {
248       return true;
249     }
250   }
251 
252   return false;
253 }
254 
findBlockedIntervals(const odb::Rect & die_area,const odb::Rect & box)255 std::vector<Interval> IOPlacer::findBlockedIntervals(const odb::Rect& die_area,
256                                                      const odb::Rect& box)
257 {
258   std::vector<Interval> intervals;
259 
260   // check intersect bottom edge
261   if (die_area.yMin() == box.yMin()) {
262     intervals.push_back(
263       Interval(Edge::bottom, box.xMin(), box.xMax()));
264   }
265   // check intersect top edge
266   if (die_area.yMax() == box.yMax()) {
267     intervals.push_back(
268       Interval(Edge::top, box.xMin(), box.xMax()));
269   }
270   // check intersect left edge
271   if (die_area.xMin() == box.xMin()) {
272     intervals.push_back(
273       Interval(Edge::left, box.yMin(), box.yMax()));
274   }
275   // check intersect right edge
276   if (die_area.xMax() == box.xMax()) {
277     intervals.push_back(
278       Interval(Edge::right, box.yMin(), box.yMax()));
279   }
280 
281   return intervals;
282 }
283 
getBlockedRegionsFromMacros()284 void IOPlacer::getBlockedRegionsFromMacros()
285 {
286   odb::Rect die_area;
287   block_->getDieArea(die_area);
288 
289   for (odb::dbInst* inst : block_->getInsts()) {
290     odb::dbMaster* master = inst->getMaster();
291     if (master->isBlock() && inst->isPlaced()) {
292       odb::Rect inst_area;
293       inst->getBBox()->getBox(inst_area);
294       odb::Rect intersect = die_area.intersect(inst_area);
295 
296       std::vector<Interval> intervals = findBlockedIntervals(die_area, intersect);
297       for (Interval interval : intervals) {
298         excludeInterval(interval);
299       }
300     }
301   }
302 }
303 
getBlockedRegionsFromDbObstructions()304 void IOPlacer::getBlockedRegionsFromDbObstructions()
305 {
306   odb::Rect die_area;
307   block_->getDieArea(die_area);
308 
309   for (odb::dbObstruction* obstruction : block_->getObstructions()) {
310     odb::dbBox* obstructBox = obstruction->getBBox();
311     odb::Rect obstructArea;
312     obstructBox->getBox(obstructArea);
313     odb::Rect intersect = die_area.intersect(obstructArea);
314 
315     std::vector<Interval> intervals = findBlockedIntervals(die_area, intersect);
316     for (Interval interval : intervals) {
317       excludeInterval(interval);
318     }
319   }
320 }
321 
findSlots(const std::set<int> & layers,Edge edge)322 void IOPlacer::findSlots(const std::set<int>& layers, Edge edge)
323 {
324   const int default_min_dist = 2;
325   Point lb = core_.getBoundary().ll();
326   Point ub = core_.getBoundary().ur();
327 
328   int lb_x = lb.x();
329   int lb_y = lb.y();
330   int ub_x = ub.x();
331   int ub_y = ub.y();
332 
333   bool vertical = (edge == Edge::top || edge == Edge::bottom);
334   int min = vertical ? lb_x : lb_y;
335   int max = vertical ? ub_x : ub_y;
336 
337   int offset = parms_->getCornerAvoidance();
338 
339   int i = 0;
340   bool dist_in_tracks = parms_->getMinDistanceInTracks();
341   for (int layer : layers) {
342     int curr_x, curr_y, start_idx, end_idx;
343     // get the on grid min distance
344     int min_dst_ver = dist_in_tracks ?
345                       core_.getMinDstPinsX()[i]*parms_->getMinDistance() :
346                       core_.getMinDstPinsX()[i]*
347                       std::ceil(static_cast<float>(parms_->getMinDistance())/core_.getMinDstPinsX()[i]);
348     int min_dst_hor = dist_in_tracks ?
349                       core_.getMinDstPinsY()[i]*parms_->getMinDistance() :
350                       core_.getMinDstPinsY()[i]*
351                       std::ceil(static_cast<float>(parms_->getMinDistance())/core_.getMinDstPinsY()[i]);
352 
353     min_dst_ver = (min_dst_ver == 0) ? default_min_dist*core_.getMinDstPinsX()[i] : min_dst_ver;
354     min_dst_hor = (min_dst_hor == 0) ? default_min_dist*core_.getMinDstPinsY()[i] : min_dst_hor;
355 
356     int min_dst_pins
357         = vertical ? min_dst_ver
358                    : min_dst_hor;
359     int init_tracks
360         = vertical ? core_.getInitTracksX()[i] : core_.getInitTracksY()[i];
361     int num_tracks
362         = vertical ? core_.getNumTracksX()[i] : core_.getNumTracksY()[i];
363 
364     float thickness_multiplier
365         = vertical ? parms_->getVerticalThicknessMultiplier()
366                    : parms_->getHorizontalThicknessMultiplier();
367 
368     int half_width = vertical ? int(ceil(core_.getMinWidthX()[i] / 2.0))
369                               : int(ceil(core_.getMinWidthY()[i] / 2.0));
370 
371     half_width *= thickness_multiplier;
372 
373     int num_tracks_offset = std::ceil(
374         offset
375         / (std::max(min_dst_ver,
376                     min_dst_hor)));
377 
378     start_idx
379         = std::max(0.0, ceil((min + half_width - init_tracks) / min_dst_pins))
380           + num_tracks_offset;
381     end_idx
382         = std::min((num_tracks - 1),
383                    static_cast<int>(floor((max - half_width - init_tracks) / min_dst_pins)))
384           - num_tracks_offset;
385     if (vertical) {
386       curr_x = init_tracks + start_idx * min_dst_pins;
387       curr_y = (edge == Edge::bottom) ? lb_y : ub_y;
388     } else {
389       curr_y = init_tracks + start_idx * min_dst_pins;
390       curr_x = (edge == Edge::left) ? lb_x : ub_x;
391     }
392 
393     std::vector<Point> slots;
394     for (int i = start_idx; i <= end_idx; ++i) {
395       Point pos(curr_x, curr_y);
396       slots.push_back(pos);
397       if (vertical) {
398         curr_x += min_dst_pins;
399       } else {
400         curr_y += min_dst_pins;
401       }
402     }
403 
404     if (edge == Edge::top || edge == Edge::left) {
405       std::reverse(slots.begin(), slots.end());
406     }
407 
408     for (Point pos : slots) {
409       curr_x = pos.getX();
410       curr_y = pos.getY();
411       bool blocked
412           = vertical ? checkBlocked(edge, curr_x) : checkBlocked(edge, curr_y);
413       slots_.push_back({blocked, false, Point(curr_x, curr_y), layer, edge});
414     }
415     i++;
416   }
417 }
418 
defineSlots()419 void IOPlacer::defineSlots()
420 {
421   Point lb = core_.getBoundary().ll();
422   Point ub = core_.getBoundary().ur();
423 
424   /*******************************************
425    *  Order of the edges when creating slots  *
426    ********************************************
427    *                 <----                    *
428    *                                          *
429    *                 3st edge     upperBound  *
430    *           *------------------x           *
431    *           |                  |           *
432    *   |       |                  |      ^    *
433    *   |  4th  |                  | 2nd  |    *
434    *   |  edge |                  | edge |    *
435    *   V       |                  |      |    *
436    *           |                  |           *
437    *           x------------------*           *
438    *   lowerBound    1st edge                 *
439    *                 ---->                    *
440    *******************************************/
441 
442   // For wider pins (when set_hor|ver_thick multiplier is used), a valid
443   // slot is one that does not cause a part of the pin to lie outside
444   // the die area (OffGrid violation):
445   // (offset + k_start * pitch) - halfWidth >= lower_bound , where k_start is a
446   // non-negative integer => start_idx is k_start (offset + k_end * pitch) +
447   // halfWidth <= upper_bound, where k_end is a non-negative integer => end_idx
448   // is k_end
449   //     ^^^^^^^^ position of tracks(slots)
450 
451   findSlots(ver_layers_, Edge::bottom);
452 
453   findSlots(hor_layers_, Edge::right);
454 
455   findSlots(ver_layers_, Edge::top);
456 
457   findSlots(hor_layers_, Edge::left);
458 
459   findSlotsForTopLayer();
460 }
461 
findSections(int begin,int end,Edge edge,std::vector<Section> & sections)462 void IOPlacer::findSections(int begin, int end, Edge edge, std::vector<Section>& sections)
463 {
464   int end_slot = 0;
465   while (end_slot < end) {
466     int blocked_slots = 0;
467     end_slot = begin + slots_per_section_ - 1;
468     if (end_slot > end) {
469       end_slot = end;
470     }
471     for (int i = begin; i <= end_slot; ++i) {
472       if (slots_[i].blocked) {
473         blocked_slots++;
474       }
475     }
476     int half_length_pt = begin + (end_slot - begin) / 2;
477     Section n_sec = {slots_.at(half_length_pt).pos};
478     n_sec.num_slots = end_slot - begin - blocked_slots + 1;
479     if (n_sec.num_slots < 0) {
480       logger_->error(PPL, 40, "Negative number of slots");
481     }
482     n_sec.begin_slot = begin;
483     n_sec.end_slot = end_slot;
484     n_sec.used_slots = 0;
485     n_sec.edge = edge;
486 
487     sections.push_back(n_sec);
488     begin = ++end_slot;
489   }
490 }
491 
createSectionsPerConstraint(const Constraint & constraint)492 std::vector<Section> IOPlacer::createSectionsPerConstraint(const Constraint &constraint)
493 {
494   const Interval &interv = constraint.interval;
495   const Edge &edge = interv.edge;
496 
497   std::vector<Section> sections;
498   if (edge != Edge::invalid) {
499     const std::set<int>& layers =
500       (edge == Edge::left || edge == Edge::right) ?
501       hor_layers_ : ver_layers_;
502 
503     for (int layer : layers) {
504       std::vector<Slot>::iterator it =
505         std::find_if(slots_.begin(), slots_.end(),
506          [&](const Slot& s) {
507           int slot_xy =
508             (edge == Edge::left || edge == Edge::right) ?
509             s.pos.y() : s.pos.x();
510           if (edge == Edge::bottom || edge == Edge::right)
511             return (s.edge == edge && s.layer == layer &&
512                     slot_xy >= interv.begin);
513           return (s.edge == edge && s.layer == layer &&
514                     slot_xy <= interv.end);
515          });
516       int constraint_begin = it - slots_.begin();
517 
518       it =
519         std::find_if(slots_.begin() + constraint_begin, slots_.end(),
520          [&](const Slot& s) {
521           int slot_xy =
522             (edge == Edge::left || edge == Edge::right) ?
523             s.pos.y() : s.pos.x();
524           if (edge == Edge::bottom || edge == Edge::right)
525             return (slot_xy >= interv.end || s.edge != edge || s.layer != layer);
526           return (slot_xy <= interv.begin || s.edge != edge || s.layer != layer);
527          });
528       int constraint_end = it - slots_.begin()-1;
529 
530       findSections(constraint_begin, constraint_end, edge, sections);
531     }
532   } else {
533     sections = findSectionsForTopLayer(constraint.box);
534   }
535 
536   return sections;
537 }
538 
createSectionsPerEdge(Edge edge,const std::set<int> & layers)539 void IOPlacer::createSectionsPerEdge(Edge edge, const std::set<int>& layers)
540 {
541   for (int layer : layers) {
542     std::vector<Slot>::iterator it = std::find_if(slots_.begin(), slots_.end(),
543                                                  [&](Slot s) {
544                                                     return s.edge == edge &&
545                                                            s.layer == layer;
546                                                  });
547     int edge_begin = it - slots_.begin();
548 
549     it = std::find_if(slots_.begin()+edge_begin, slots_.end(),
550                                                  [&](Slot s) {
551                                                     return s.edge != edge ||
552                                                            s.layer != layer;
553                                                  });
554     int edge_end = it - slots_.begin() - 1;
555 
556     findSections(edge_begin, edge_end, edge, sections_);
557   }
558 }
559 
createSections()560 void IOPlacer::createSections()
561 {
562   sections_.clear();
563 
564   // sections only have slots at the same edge of the die boundary
565   createSectionsPerEdge(Edge::bottom, ver_layers_);
566   createSectionsPerEdge(Edge::right, hor_layers_);
567   createSectionsPerEdge(Edge::top, ver_layers_);
568   createSectionsPerEdge(Edge::left, hor_layers_);
569 }
570 
assignConstrainedPinsToSections(Constraint & constraint)571 std::vector<Section> IOPlacer::assignConstrainedPinsToSections(Constraint &constraint)
572 {
573   bool top_layer = constraint.interval.edge == Edge::invalid;
574   std::vector<Slot> &slots = top_layer ? top_layer_slots_ : slots_;
575   Netlist& netlist = netlist_io_pins_;
576   assignConstrainedGroupsToSections(constraint, constraint.sections);
577 
578   int total_slots_count = 0;
579   for (Section& sec : constraint.sections) {
580     int new_slots_count = 0;
581     for (int slot_idx = sec.begin_slot; slot_idx <= sec.end_slot; slot_idx++) {
582       new_slots_count += (slots[slot_idx].blocked || slots[slot_idx].used) ? 0 : 1;
583     }
584     sec.num_slots = new_slots_count;
585     total_slots_count += new_slots_count;
586   }
587 
588   std::vector<int> pin_indices = findPinsForConstraint(constraint, netlist);
589 
590   if (pin_indices.size() > total_slots_count) {
591     logger_->error(PPL, 74, "Number of pins ({}) exceed number of valid positions ({}) for constraint.", pin_indices.size(), total_slots_count);
592   }
593 
594   for (int idx : pin_indices) {
595     IOPin& io_pin = netlist.getIoPin(idx);
596     assignPinToSection(io_pin, idx, constraint.sections);
597   }
598 
599   return constraint.sections;
600 }
601 
assignConstrainedGroupsToSections(Constraint & constraint,std::vector<Section> & sections)602 void IOPlacer::assignConstrainedGroupsToSections(Constraint &constraint,
603                                                  std::vector<Section> &sections)
604 {
605   int total_pins_assigned = 0;
606   Netlist& net = netlist_io_pins_;
607 
608   for (std::vector<int>& io_group : net.getIOGroups()) {
609     const PinList& pin_list = constraint.pin_list;
610     IOPin& io_pin = net.getIoPin(io_group[0]);
611 
612     if (std::find(pin_list.begin(), pin_list.end(), io_pin.getBTerm()) != pin_list.end()) {
613       total_pins_assigned += assignGroupToSection(io_group, sections);
614     }
615   }
616 }
617 
assignGroupsToSections()618 int IOPlacer::assignGroupsToSections()
619 {
620   int total_pins_assigned = 0;
621   Netlist& net = netlist_io_pins_;
622   std::vector<Section>& sections = sections_;
623 
624   int total_groups_assigned = 0;
625 
626   for (std::vector<int>& io_group : net.getIOGroups()) {
627     total_pins_assigned += assignGroupToSection(io_group, sections);
628     total_groups_assigned++;
629   }
630 
631   return total_pins_assigned;
632 }
633 
assignGroupToSection(const std::vector<int> & io_group,std::vector<Section> & sections)634 int IOPlacer::assignGroupToSection(const std::vector<int> &io_group,
635                                    std::vector<Section> &sections) {
636   Netlist& net = netlist_io_pins_;
637   int group_size = io_group.size();
638   bool group_assigned = false;
639   int total_pins_assigned = 0;
640 
641   IOPin &io_pin = net.getIoPin(io_group[0]);
642 
643   if (!io_pin.isAssignedToSection()) {
644     std::vector<int64_t> dst(sections.size(), 0);
645     for (int i = 0; i < sections.size(); i++) {
646       for (int pin_idx : io_group) {
647         int pin_hpwl = net.computeIONetHPWL(
648             pin_idx, sections[i].pos);
649         if (pin_hpwl == std::numeric_limits<int>::max()) {
650           dst[i] = pin_hpwl;
651           break;
652         } else {
653           dst[i] += pin_hpwl;
654         }
655       }
656     }
657     for (auto i : sortIndexes(dst)) {
658       if (sections[i].used_slots+group_size < sections[i].num_slots) {
659         std::vector<int> group;
660         for (int pin_idx : io_group) {
661           IOPin &io_pin = net.getIoPin(pin_idx);
662 
663           std::vector<InstancePin> inst_pins_vector;
664           net.getSinksOfIO(pin_idx, inst_pins_vector);
665 
666           sections[i].net.addIONet(io_pin, inst_pins_vector);
667           group.push_back(sections[i].net.numIOPins()-1);
668           sections[i].used_slots++;
669           io_pin.assignToSection();
670         }
671         total_pins_assigned += group_size;
672         sections[i].net.addIOGroup(group);
673         group_assigned = true;
674         break;
675       }
676       // Try to add pin just to first
677       if (!force_pin_spread_)
678         break;
679     }
680     if (!group_assigned) {
681       logger_->error(PPL, 42, "Unsuccessfully assigned I/O groups");
682     }
683   }
684 
685   return total_pins_assigned;
686 }
687 
assignPinsToSections(int assigned_pins_count)688 bool IOPlacer::assignPinsToSections(int assigned_pins_count)
689 {
690   Netlist& net = netlist_io_pins_;
691   std::vector<Section>& sections = sections_;
692 
693   createSections();
694 
695   int total_pins_assigned = assignGroupsToSections();
696 
697   int idx = 0;
698   for (IOPin& io_pin : net.getIOPins()) {
699     if (assignPinToSection(io_pin, idx, sections)) {
700       total_pins_assigned++;
701     }
702     idx++;
703   }
704 
705   total_pins_assigned += assigned_pins_count;
706 
707   if (total_pins_assigned == net.numIOPins()) {
708     logger_->report("Successfully assigned I/O pins");
709     return true;
710   } else {
711     logger_->report("Unsuccessfully assigned I/O pins ({} out of {})", total_pins_assigned, net.numIOPins());
712     return false;
713   }
714 }
715 
assignPinToSection(IOPin & io_pin,int idx,std::vector<Section> & sections)716 bool IOPlacer::assignPinToSection(IOPin& io_pin, int idx, std::vector<Section>& sections)
717 {
718   Netlist& net = netlist_io_pins_;
719   bool pin_assigned = false;
720 
721   if (!io_pin.isInGroup() && !io_pin.isAssignedToSection()) {
722     std::vector<int> dst(sections.size());
723     std::vector<InstancePin> inst_pins_vector;
724     for (int i = 0; i < sections.size(); i++) {
725       dst[i] = net.computeIONetHPWL(
726           idx, sections[i].pos);
727     }
728     net.getSinksOfIO(idx, inst_pins_vector);
729     for (auto i : sortIndexes(dst)) {
730       if (sections[i].used_slots < sections[i].num_slots) {
731         sections[i].net.addIONet(io_pin, inst_pins_vector);
732         sections[i].used_slots++;
733         pin_assigned = true;
734         io_pin.assignToSection();
735         break;
736       }
737       // Try to add pin just to first
738       if (!force_pin_spread_)
739         break;
740     }
741   }
742 
743   return pin_assigned;
744 }
745 
printConfig()746 void IOPlacer::printConfig()
747 {
748   logger_->info(PPL, 1, "Number of slots          {}", slots_.size());
749   logger_->info(PPL, 2, "Number of I/O            {}", netlist_.numIOPins());
750   logger_->info(
751       PPL, 3, "Number of I/O w/sink     {}", netlist_io_pins_.numIOPins());
752   logger_->info(PPL, 4, "Number of I/O w/o sink   {}", zero_sink_ios_.size());
753   logger_->info(PPL, 5, "Slots per section        {}", slots_per_section_);
754   logger_->info(PPL, 6, "Slots increase factor    {:.1}", slots_increase_factor_);
755   logger_->info(PPL, 8, "Usage increase factor    {:.1}", usage_increase_factor_);
756   logger_->info(PPL, 9, "Force pin spread         {}", force_pin_spread_);
757 }
758 
setupSections(int assigned_pins_count)759 void IOPlacer::setupSections(int assigned_pins_count)
760 {
761   bool all_assigned;
762   int i = 0;
763 
764   do {
765     logger_->info(PPL, 10, "Tentative {} to set up sections", i++);
766     printConfig();
767 
768     all_assigned = assignPinsToSections(assigned_pins_count);
769 
770     slots_per_section_ *= (1 + slots_increase_factor_);
771     if (sections_.size() > MAX_SECTIONS_RECOMMENDED) {
772       logger_->warn(PPL,
773                     36,
774                     "Number of sections is {}"
775                     " while the maximum recommended value is {}"
776                     " this may negatively affect performance",
777                     sections_.size(),
778                     MAX_SECTIONS_RECOMMENDED);
779     }
780     if (slots_per_section_ > MAX_SLOTS_RECOMMENDED) {
781       logger_->warn(PPL,
782                     37,
783                     "Number of slots per sections is {}"
784                     " while the maximum recommended value is {}"
785                     " this may negatively affect performance",
786                     slots_per_section_,
787                     MAX_SLOTS_RECOMMENDED);
788     }
789   } while (!all_assigned);
790 }
791 
updateOrientation(IOPin & pin)792 void IOPlacer::updateOrientation(IOPin& pin)
793 {
794   const int x = pin.getX();
795   const int y = pin.getY();
796   int lower_x_bound = core_.getBoundary().ll().x();
797   int lower_y_bound = core_.getBoundary().ll().y();
798   int upper_x_bound = core_.getBoundary().ur().x();
799   int upper_y_bound = core_.getBoundary().ur().y();
800 
801   if (x == lower_x_bound) {
802     if (y == upper_y_bound) {
803       pin.setOrientation(Orientation::south);
804       return;
805     } else {
806       pin.setOrientation(Orientation::east);
807       return;
808     }
809   }
810   if (x == upper_x_bound) {
811     if (y == lower_y_bound) {
812       pin.setOrientation(Orientation::north);
813       return;
814     } else {
815       pin.setOrientation(Orientation::west);
816       return;
817     }
818   }
819   if (y == lower_y_bound) {
820     pin.setOrientation(Orientation::north);
821     return;
822   }
823   if (y == upper_y_bound) {
824     pin.setOrientation(Orientation::south);
825     return;
826   }
827 }
828 
updatePinArea(IOPin & pin)829 void IOPlacer::updatePinArea(IOPin& pin)
830 {
831   const int mfg_grid = tech_->getManufacturingGrid();
832 
833   if (mfg_grid == 0) {
834     logger_->error(PPL, 20, "Manufacturing grid is not defined.");
835   }
836 
837   if (pin.getLayer() != top_grid_.layer) {
838     int index;
839 
840     int i = 0;
841     for (int layer : hor_layers_) {
842       if (layer == pin.getLayer())
843         index = i;
844       i++;
845     }
846 
847     i = 0;
848     for (int layer : ver_layers_) {
849       if (layer == pin.getLayer())
850         index = i;
851       i++;
852     }
853 
854     if (pin.getOrientation() == Orientation::north
855         || pin.getOrientation() == Orientation::south) {
856       float thickness_multiplier = parms_->getVerticalThicknessMultiplier();
857       int half_width
858           = int(ceil(core_.getMinWidthX()[index] / 2.0)) * thickness_multiplier;
859       int height
860           = int(std::max(2.0 * half_width,
861                          ceil(core_.getMinAreaX()[index] / (2.0 * half_width))));
862 
863       int ext = 0;
864       if (parms_->getVerticalLength() != -1) {
865         height = parms_->getVerticalLength() * core_.getDatabaseUnit();
866       }
867 
868       if (parms_->getVerticalLengthExtend() != -1) {
869         ext = parms_->getVerticalLengthExtend() * core_.getDatabaseUnit();
870       }
871 
872       if (height % mfg_grid != 0) {
873         height = mfg_grid*std::ceil(static_cast<float>(height)/mfg_grid);
874       }
875 
876       if (pin.getOrientation() == Orientation::north) {
877         pin.setLowerBound(pin.getX() - half_width, pin.getY() - ext);
878         pin.setUpperBound(pin.getX() + half_width, pin.getY() + height);
879       } else {
880         pin.setLowerBound(pin.getX() - half_width, pin.getY() + ext);
881         pin.setUpperBound(pin.getX() + half_width, pin.getY() - height);
882       }
883     }
884 
885     if (pin.getOrientation() == Orientation::west
886         || pin.getOrientation() == Orientation::east) {
887       float thickness_multiplier = parms_->getHorizontalThicknessMultiplier();
888       int half_width
889           = int(ceil(core_.getMinWidthY()[index] / 2.0)) * thickness_multiplier;
890       int height
891           = int(std::max(2.0 * half_width,
892                          ceil(core_.getMinAreaY()[index] / (2.0 * half_width))));
893 
894       int ext = 0;
895       if (parms_->getHorizontalLengthExtend() != -1) {
896         ext = parms_->getHorizontalLengthExtend() * core_.getDatabaseUnit();
897       }
898       if (parms_->getHorizontalLength() != -1) {
899         height = parms_->getHorizontalLength() * core_.getDatabaseUnit();
900       }
901 
902       if (height % mfg_grid != 0) {
903         height = mfg_grid*std::ceil(static_cast<float>(height)/mfg_grid);
904       }
905 
906       if (pin.getOrientation() == Orientation::east) {
907         pin.setLowerBound(pin.getX() - ext, pin.getY() - half_width);
908         pin.setUpperBound(pin.getX() + height, pin.getY() + half_width);
909       } else {
910         pin.setLowerBound(pin.getX() - height, pin.getY() - half_width);
911         pin.setUpperBound(pin.getX() + ext, pin.getY() + half_width);
912       }
913     }
914   } else {
915     int width = top_grid_.width;
916     int height = top_grid_.height;
917 
918     if (width % mfg_grid != 0) {
919       width = mfg_grid*std::ceil(static_cast<float>(width)/mfg_grid);
920     }
921 
922     if (height % mfg_grid != 0) {
923       height = mfg_grid*std::ceil(static_cast<float>(height)/mfg_grid);
924     }
925 
926     pin.setLowerBound(pin.getX() - width/2, pin.getY() - height/2);
927     pin.setUpperBound(pin.getX() + width/2, pin.getY() + height/2);
928   }
929 }
930 
returnIONetsHPWL(Netlist & netlist)931 int IOPlacer::returnIONetsHPWL(Netlist& netlist)
932 {
933   int pin_index = 0;
934   int hpwl = 0;
935   int idx = 0;
936   for (IOPin& io_pin : netlist.getIOPins()) {
937     hpwl += netlist.computeIONetHPWL(idx, io_pin.getPosition());
938     pin_index++;
939     idx++;
940   }
941 
942   return hpwl;
943 }
944 
returnIONetsHPWL()945 int IOPlacer::returnIONetsHPWL()
946 {
947   return returnIONetsHPWL(netlist_);
948 }
949 
excludeInterval(Edge edge,int begin,int end)950 void IOPlacer::excludeInterval(Edge edge, int begin, int end)
951 {
952   Interval excluded_interv = Interval(edge, begin, end);
953 
954   excluded_intervals_.push_back(excluded_interv);
955 }
956 
excludeInterval(Interval interval)957 void IOPlacer::excludeInterval(Interval interval)
958 {
959   excluded_intervals_.push_back(interval);
960 }
961 
addNamesConstraint(PinList * pins,Edge edge,int begin,int end)962 void IOPlacer::addNamesConstraint(PinList* pins, Edge edge, int begin, int end)
963 {
964   Interval interval(edge, begin, end);
965   constraints_.push_back(Constraint(*pins, Direction::invalid, interval));;
966 }
967 
addDirectionConstraint(Direction direction,Edge edge,int begin,int end)968 void IOPlacer::addDirectionConstraint(Direction direction,
969                                       Edge edge,
970                                       int begin,
971                                       int end)
972 {
973   Interval interval(edge, begin, end);
974   Constraint constraint(PinList(), direction, interval);
975   constraints_.push_back(constraint);
976 }
977 
addTopLayerConstraint(PinList * pins,int x1,int y1,int x2,int y2)978 void IOPlacer::addTopLayerConstraint(PinList* pins,
979                                      int x1, int y1,
980                                      int x2, int y2)
981 {
982   odb::Rect box = odb::Rect(x1,y1, x2, y2);
983   Constraint constraint(*pins, Direction::invalid, box);
984   constraints_.push_back(constraint);
985 }
986 
getPinsFromDirectionConstraint(Constraint & constraint)987 void IOPlacer::getPinsFromDirectionConstraint(Constraint &constraint)
988 {
989   Netlist& netlist = netlist_io_pins_;
990   if (constraint.direction != Direction::invalid &&
991       constraint.pin_list.empty()) {
992     for (const IOPin& io_pin : netlist.getIOPins()) {
993       if (io_pin.getDirection() == constraint.direction) {
994         constraint.pin_list.insert(io_pin.getBTerm());
995       }
996     }
997   }
998 }
999 
findPinsForConstraint(const Constraint & constraint,Netlist & netlist)1000 std::vector<int> IOPlacer::findPinsForConstraint(const Constraint &constraint, Netlist& netlist)
1001 {
1002   std::vector<int> pin_indices;
1003   const PinList &pin_list = constraint.pin_list;
1004   for (odb::dbBTerm* bterm : pin_list) {
1005     if (bterm->getFirstPinPlacementStatus().isFixed()){
1006       continue;
1007     }
1008     int idx = netlist.getIoPinIdx(bterm);
1009     IOPin& io_pin = netlist.getIoPin(idx);
1010     if (!io_pin.isPlaced() && !io_pin.isAssignedToSection()) {
1011       pin_indices.push_back(idx);
1012     } else if (!io_pin.isInGroup()) {
1013       logger_->warn(PPL, 75, "Pin {} is assigned to more than one constraints. Using last defined constraint.", io_pin.getName());
1014     }
1015   }
1016 
1017   return pin_indices;
1018 }
1019 
initConstraints()1020 void IOPlacer::initConstraints()
1021 {
1022   std::reverse(constraints_.begin(), constraints_.end());
1023   for (Constraint &constraint : constraints_) {
1024     getPinsFromDirectionConstraint(constraint);
1025     constraint.sections = createSectionsPerConstraint(constraint);
1026     int num_slots = 0;
1027     for (Section sec : constraint.sections) {
1028       num_slots += sec.num_slots;
1029     }
1030     if (num_slots > 0) {
1031       constraint.pins_per_slots = static_cast<float>(constraint.pin_list.size())/num_slots;
1032     } else {
1033       logger_->error(PPL, 76, "Constraint does not have available slots for its pins.");
1034     }
1035   }
1036   sortConstraints();
1037 }
1038 
sortConstraints()1039 void IOPlacer::sortConstraints() {
1040   std::stable_sort(constraints_.begin(),
1041                    constraints_.end(),
1042                    [&](const Constraint& c1, const Constraint& c2) {
1043                       // treat every non-overlapping constraint as equal, so stable_sort keeps the user order
1044                       return (c1.pins_per_slots < c2.pins_per_slots) && overlappingConstraints(c1, c2);
1045                    });
1046 }
1047 
overlappingConstraints(const Constraint & c1,const Constraint & c2)1048 bool IOPlacer::overlappingConstraints(const Constraint& c1, const Constraint& c2) {
1049   const Interval& interv1 = c1.interval;
1050   const Interval& interv2 = c2.interval;
1051 
1052   if (interv1.edge == interv2.edge) {
1053     return std::max(interv1.begin, interv2.begin) <= std::min(interv1.end, interv2.end);
1054   }
1055 
1056   return false;
1057 }
1058 
getEdge(std::string edge)1059 Edge IOPlacer::getEdge(std::string edge)
1060 {
1061   if (edge == "top") {
1062     return Edge::top;
1063   } else if (edge == "bottom") {
1064     return Edge::bottom;
1065   } else if (edge == "left") {
1066     return Edge::left;
1067   } else {
1068     return Edge::right;
1069   }
1070 
1071   return Edge::invalid;
1072 }
1073 
getDirection(std::string direction)1074 Direction IOPlacer::getDirection(std::string direction)
1075 {
1076   if (direction == "input") {
1077     return Direction::input;
1078   } else if (direction == "output") {
1079     return Direction::output;
1080   } else if (direction == "inout") {
1081     return Direction::inout;
1082   } else {
1083     return Direction::feedthru;
1084   }
1085 
1086   return Direction::invalid;
1087 }
1088 
addPinGroup(PinGroup * group)1089 void IOPlacer::addPinGroup(PinGroup* group)
1090 {
1091   pin_groups_.push_back(*group);
1092 }
1093 
findPinAssignment(std::vector<Section> & sections)1094 void IOPlacer::findPinAssignment(std::vector<Section>& sections)
1095 {
1096   std::vector<HungarianMatching> hg_vec;
1097   for (int idx = 0; idx < sections.size(); idx++) {
1098     if (sections[idx].net.numIOPins() > 0) {
1099       if (sections[idx].edge == Edge::invalid) {
1100         HungarianMatching hg(sections[idx], top_layer_slots_, logger_);
1101         hg_vec.push_back(hg);
1102       } else {
1103         HungarianMatching hg(sections[idx], slots_, logger_);
1104         hg_vec.push_back(hg);
1105       }
1106     }
1107   }
1108 
1109   for (int idx = 0; idx < hg_vec.size(); idx++) {
1110     hg_vec[idx].findAssignmentForGroups();
1111   }
1112 
1113   for (int idx = 0; idx < hg_vec.size(); idx++) {
1114     hg_vec[idx].getAssignmentForGroups(assignment_);
1115   }
1116 
1117   for (int idx = 0; idx < hg_vec.size(); idx++) {
1118     hg_vec[idx].findAssignment();
1119   }
1120 
1121   for (int idx = 0; idx < hg_vec.size(); idx++) {
1122     hg_vec[idx].getFinalAssignment(assignment_);
1123   }
1124 }
1125 
updateSlots()1126 void IOPlacer::updateSlots()
1127 {
1128   for (Slot& slot : slots_) {
1129     slot.blocked = slot.used;
1130   }
1131   for (Slot& slot : top_layer_slots_) {
1132     slot.blocked = slot.used;
1133   }
1134 }
1135 
run(bool random_mode)1136 void IOPlacer::run(bool random_mode)
1137 {
1138   initParms();
1139 
1140   initNetlistAndCore(hor_layers_, ver_layers_);
1141   getBlockedRegionsFromMacros();
1142 
1143   int init_hpwl = 0;
1144   int total_hpwl = 0;
1145   int delta_hpwl = 0;
1146 
1147   initIOLists();
1148   defineSlots();
1149 
1150   initConstraints();
1151 
1152   if (report_hpwl_) {
1153     init_hpwl = returnIONetsHPWL(netlist_);
1154   }
1155   if (random_mode) {
1156     logger_->info(PPL, 7, "Random pin placement.");
1157     randomPlacement();
1158   } else {
1159     int constrained_pins_cnt = 0;
1160     for (Constraint &constraint : constraints_) {
1161       std::vector<Section> sections_for_constraint = assignConstrainedPinsToSections(constraint);
1162       for (Section& sec : sections_for_constraint) {
1163         constrained_pins_cnt += sec.net.numIOPins();
1164       }
1165 
1166       findPinAssignment(sections_for_constraint);
1167       updateSlots();
1168     }
1169 
1170     setupSections(constrained_pins_cnt);
1171 
1172     findPinAssignment(sections_);
1173   }
1174 
1175   for (int i = 0; i < assignment_.size(); ++i) {
1176     updateOrientation(assignment_[i]);
1177     updatePinArea(assignment_[i]);
1178   }
1179 
1180   if (assignment_.size() != static_cast<int>(netlist_.numIOPins())) {
1181     logger_->error(PPL,
1182                    39,
1183                    "Assigned {} pins out of {} IO pins",
1184                    assignment_.size(),
1185                    netlist_.numIOPins());
1186   }
1187 
1188   if (report_hpwl_) {
1189     for (int idx = 0; idx < sections_.size(); idx++) {
1190       total_hpwl += returnIONetsHPWL(sections_[idx].net);
1191     }
1192     delta_hpwl = init_hpwl - total_hpwl;
1193     logger_->info(PPL, 11, "HPWL before ioPlacer: {}", init_hpwl);
1194     logger_->info(PPL, 12, "HPWL after  ioPlacer: {}", total_hpwl);
1195     logger_->info(PPL, 13, "HPWL delta  ioPlacer: {}", delta_hpwl);
1196   }
1197 
1198   commitIOPlacementToDB(assignment_);
1199   clear();
1200 }
1201 
placePin(odb::dbBTerm * bterm,int layer,int x,int y,int width,int height)1202 void IOPlacer::placePin(odb::dbBTerm* bterm, int layer, int x, int y, int width, int height)
1203 {
1204   tech_ = db_->getTech();
1205   block_ = db_->getChip()->getBlock();
1206   const int mfg_grid = tech_->getManufacturingGrid();
1207   if (width % mfg_grid != 0) {
1208     width = mfg_grid*std::ceil(static_cast<float>(width)/mfg_grid);
1209   }
1210 
1211   if (height % mfg_grid != 0) {
1212     height = mfg_grid*std::ceil(static_cast<float>(height)/mfg_grid);
1213   }
1214 
1215   odb::Point pos = odb::Point(x, y);
1216   odb::Point ll = odb::Point(pos.x() - width/2, pos.y() - height/2);
1217   odb::Point ur = odb::Point(pos.x() + width/2, pos.y() + height/2);
1218 
1219   IOPin io_pin = IOPin(bterm, pos, Direction::invalid, ll, ur, odb::dbPlacementStatus::FIRM);
1220   io_pin.setLayer(layer);
1221 
1222   commitIOPinToDB(io_pin);
1223 
1224   logger_->info(PPL, 70, "Pin {} placed at ({}um, {}um)", bterm->getName(), x/tech_->getLefUnits(), y/tech_->getLefUnits());
1225 }
1226 
1227 // db functions
populateIOPlacer(std::set<int> hor_layer_idx,std::set<int> ver_layer_idx)1228 void IOPlacer::populateIOPlacer(std::set<int> hor_layer_idx,
1229                                 std::set<int> ver_layer_idx)
1230 {
1231   if (tech_ == nullptr) {
1232     tech_ = db_->getTech();
1233   }
1234 
1235   if (block_ == nullptr) {
1236     block_ = db_->getChip()->getBlock();
1237   }
1238 
1239   initCore(hor_layer_idx, ver_layer_idx);
1240   initNetlist();
1241 }
1242 
initCore(std::set<int> hor_layer_idxs,std::set<int> ver_layer_idxs)1243 void IOPlacer::initCore(std::set<int> hor_layer_idxs,
1244                         std::set<int> ver_layer_idxs)
1245 {
1246   int database_unit = tech_->getLefUnits();
1247 
1248   Rect boundary;
1249   block_->getDieArea(boundary);
1250 
1251   std::vector<int> min_spacings_x;
1252   std::vector<int> min_spacings_y;
1253   std::vector<int> init_tracks_x;
1254   std::vector<int> init_tracks_y;
1255   std::vector<int> min_areas_x;
1256   std::vector<int> min_areas_y;
1257   std::vector<int> min_widths_x;
1258   std::vector<int> min_widths_y;
1259   std::vector<int> num_tracks_x;
1260   std::vector<int> num_tracks_y;
1261 
1262   for (int hor_layer_idx : hor_layer_idxs) {
1263     int min_spacing_y = 0;
1264     int init_track_y = 0;
1265     int min_area_y = 0;
1266     int min_width_y = 0;
1267     int num_track_y = 0;
1268 
1269     odb::dbTechLayer* hor_layer = tech_->findRoutingLayer(hor_layer_idx);
1270     odb::dbTrackGrid* hor_track_grid = block_->findTrackGrid(hor_layer);
1271     hor_track_grid->getGridPatternY(
1272         0, init_track_y, num_track_y, min_spacing_y);
1273 
1274     min_area_y = hor_layer->getArea() * database_unit * database_unit;
1275     min_width_y = hor_layer->getWidth();
1276 
1277     min_spacings_y.push_back(min_spacing_y);
1278     init_tracks_y.push_back(init_track_y);
1279     min_areas_y.push_back(min_area_y);
1280     min_widths_y.push_back(min_width_y);
1281     num_tracks_y.push_back(num_track_y);
1282   }
1283 
1284   for (int ver_layer_idx : ver_layer_idxs) {
1285     int min_spacing_x = 0;
1286     int init_track_x = 0;
1287     int min_area_x = 0;
1288     int min_width_x = 0;
1289     int num_track_x = 0;
1290 
1291     odb::dbTechLayer* ver_layer = tech_->findRoutingLayer(ver_layer_idx);
1292     odb::dbTrackGrid* ver_track_grid = block_->findTrackGrid(ver_layer);
1293     ver_track_grid->getGridPatternX(
1294         0, init_track_x, num_track_x, min_spacing_x);
1295 
1296     min_area_x = ver_layer->getArea() * database_unit * database_unit;
1297     min_width_x = ver_layer->getWidth();
1298 
1299     min_spacings_x.push_back(min_spacing_x);
1300     init_tracks_x.push_back(init_track_x);
1301     min_areas_x.push_back(min_area_x);
1302     min_widths_x.push_back(min_width_x);
1303     num_tracks_x.push_back(num_track_x);
1304   }
1305 
1306   core_ = Core(boundary,
1307                min_spacings_x,
1308                min_spacings_y,
1309                init_tracks_x,
1310                init_tracks_y,
1311                num_tracks_x,
1312                num_tracks_y,
1313                min_areas_x,
1314                min_areas_y,
1315                min_widths_x,
1316                min_widths_y,
1317                database_unit);
1318 }
1319 
addTopLayerPinPattern(int layer,int x_step,int y_step,int llx,int lly,int urx,int ury,int width,int height,int keepout)1320 void IOPlacer::addTopLayerPinPattern(int layer, int x_step, int y_step,
1321                                      int llx, int lly, int urx, int ury,
1322                                      int width, int height, int keepout)
1323 {
1324   top_grid_ = TopLayerGrid(layer, x_step, y_step, llx, lly, urx, ury, width, height, keepout);
1325 }
1326 
findSlotsForTopLayer()1327 void IOPlacer::findSlotsForTopLayer()
1328 {
1329   if (top_layer_slots_.empty() && top_grid_.width > 0) {
1330     for (int x = top_grid_.llx; x < top_grid_.urx; x += top_grid_.x_step) {
1331       for (int y = top_grid_.lly; y < top_grid_.ury; y += top_grid_.y_step) {
1332         top_layer_slots_.push_back({false, false, Point(x, y), top_grid_.layer, Edge::invalid});
1333       }
1334     }
1335 
1336     filterObstructedSlotsForTopLayer();
1337   }
1338 }
1339 
filterObstructedSlotsForTopLayer()1340 void IOPlacer::filterObstructedSlotsForTopLayer()
1341 {
1342   // Collect top_grid_ obstructions
1343   std::vector<odb::Rect> obstructions;
1344 
1345   // Get routing obstructions
1346   for (odb::dbObstruction* obstruction : block_->getObstructions()) {
1347     odb::dbBox* box = obstruction->getBBox();
1348     if (box->getTechLayer()->getRoutingLevel() == top_grid_.layer) {
1349       odb::Rect obstruction_rect;
1350       box->getBox(obstruction_rect);
1351       obstructions.push_back(obstruction_rect);
1352     }
1353   }
1354 
1355   // Get already routed special nets
1356   for (odb::dbNet* net : block_->getNets()) {
1357     if (net->isSpecial()) {
1358       for (odb::dbSWire* swire : net->getSWires()) {
1359         for (odb::dbSBox* wire : swire->getWires()) {
1360           if (!wire->isVia()) {
1361             if (wire->getTechLayer()->getRoutingLevel() == top_grid_.layer) {
1362               odb::Rect obstruction_rect;
1363               wire->getBox(obstruction_rect);
1364               obstructions.push_back(obstruction_rect);
1365             }
1366           }
1367         }
1368       }
1369     }
1370   }
1371 
1372   // Get already placed pins
1373   for (odb::dbBTerm* term : block_->getBTerms()) {
1374     for (odb::dbBPin* pin : term->getBPins()) {
1375       if (pin->getPlacementStatus().isFixed()) {
1376         for (odb::dbBox* box : pin->getBoxes()) {
1377           if (box->getTechLayer()->getRoutingLevel() == top_grid_.layer) {
1378             odb::Rect obstruction_rect;
1379             box->getBox(obstruction_rect);
1380             obstructions.push_back(obstruction_rect);
1381           }
1382         }
1383       }
1384     }
1385   }
1386 
1387   // check for slots that go beyond the die boundary
1388   odb::Rect die_area;
1389   block_->getDieArea(die_area);
1390   for (auto& slot : top_layer_slots_) {
1391     odb::Point& point = slot.pos;
1392     if (point.x() - top_grid_.width/2 < die_area.xMin()
1393         || point.y() - top_grid_.height/2 < die_area.yMin()
1394         || point.x() + top_grid_.width/2 > die_area.xMax()
1395         || point.y() + top_grid_.height/2 > die_area.yMax()) {
1396       // mark slot as blocked since it extends beyond the die area
1397       slot.blocked = true;
1398     }
1399   }
1400 
1401   // check for slots that overlap with obstructions
1402   for (odb::Rect& rect : obstructions) {
1403     for (auto& slot : top_layer_slots_) {
1404       odb::Point& point = slot.pos;
1405       // mock slot with keepout
1406       odb::Rect pin_rect(point.x() - top_grid_.width/2  - top_grid_.keepout,
1407                          point.y() - top_grid_.height/2 - top_grid_.keepout,
1408                          point.x() + top_grid_.width/2  + top_grid_.keepout,
1409                          point.y() + top_grid_.height/2 + top_grid_.keepout);
1410       if (rect.intersects(pin_rect)) { // mark slot as blocked
1411         slot.blocked = true;
1412       }
1413     }
1414   }
1415 }
1416 
findSectionsForTopLayer(const odb::Rect & region)1417 std::vector<Section> IOPlacer::findSectionsForTopLayer(const odb::Rect& region)
1418 {
1419   int lb_x = region.xMin();
1420   int lb_y = region.yMin();
1421   int ub_x = region.xMax();
1422   int ub_y = region.yMax();
1423 
1424   std::vector<Section> sections;
1425   for (int x = top_grid_.llx; x < top_grid_.urx; x += top_grid_.x_step) {
1426     std::vector<Slot>& slots = top_layer_slots_;
1427     std::vector<Slot>::iterator it = std::find_if(slots.begin(), slots.end(),
1428                                                    [&](Slot s) {
1429                                                       return (s.pos.x() >= x &&
1430                                                               s.pos.x() >= lb_x &&
1431                                                               s.pos.y() >= lb_y);
1432                                                    });
1433     int edge_begin = it - slots.begin();
1434     int edge_x = slots[edge_begin].pos.x();
1435 
1436     it = std::find_if(slots.begin()+edge_begin, slots.end(),
1437                                                  [&](Slot s) {
1438                                                     return s.pos.x() != edge_x ||
1439                                                            s.pos.x() >= ub_x ||
1440                                                            s.pos.y() >= ub_y;
1441                                                  });
1442     int edge_end = it - slots.begin() - 1;
1443 
1444     int end_slot = 0;
1445 
1446     while (end_slot < edge_end) {
1447       int blocked_slots = 0;
1448       end_slot = edge_begin + slots_per_section_ - 1;
1449       if (end_slot > edge_end) {
1450         end_slot = edge_end;
1451       }
1452       for (int i = edge_begin; i <= end_slot; ++i) {
1453         if (slots[i].blocked) {
1454           blocked_slots++;
1455         }
1456       }
1457       int half_length_pt = edge_begin + (end_slot - edge_begin) / 2;
1458       Section n_sec = {slots.at(half_length_pt).pos};
1459       n_sec.num_slots = end_slot - edge_begin - blocked_slots + 1;
1460       n_sec.begin_slot = edge_begin;
1461       n_sec.end_slot = end_slot;
1462       n_sec.used_slots = 0;
1463       n_sec.edge = Edge::invalid;
1464 
1465       sections.push_back(n_sec);
1466       edge_begin = ++end_slot;
1467     }
1468   }
1469 
1470   return sections;
1471 }
1472 
initNetlist()1473 void IOPlacer::initNetlist()
1474 {
1475   const Rect& coreBoundary = core_.getBoundary();
1476   int x_center = (coreBoundary.xMin() + coreBoundary.xMax()) / 2;
1477   int y_center = (coreBoundary.yMin() + coreBoundary.yMax()) / 2;
1478 
1479   odb::dbSet<odb::dbBTerm> bterms = block_->getBTerms();
1480 
1481   for (odb::dbBTerm* b_term : bterms) {
1482     if (b_term->getFirstPinPlacementStatus().isFixed()) {
1483       continue;
1484     }
1485     odb::dbNet* net = b_term->getNet();
1486     if (net == nullptr) {
1487       logger_->warn(PPL, 38, "Pin {} without net", b_term->getConstName());
1488       continue;
1489     }
1490 
1491     Direction dir = Direction::inout;
1492     switch (b_term->getIoType()) {
1493       case odb::dbIoType::INPUT:
1494         dir = Direction::input;
1495         break;
1496       case odb::dbIoType::OUTPUT:
1497         dir = Direction::output;
1498         break;
1499       default:
1500         dir = Direction::inout;
1501     }
1502 
1503     int x_pos = 0;
1504     int y_pos = 0;
1505     b_term->getFirstPinLocation(x_pos, y_pos);
1506 
1507     Point bounds(0, 0);
1508     IOPin io_pin(b_term,
1509                  Point(x_pos, y_pos),
1510                  dir,
1511                  bounds,
1512                  bounds,
1513                  odb::dbPlacementStatus::PLACED);
1514 
1515     std::vector<InstancePin> inst_pins;
1516     odb::dbSet<odb::dbITerm> iterms = net->getITerms();
1517     odb::dbSet<odb::dbITerm>::iterator i_iter;
1518     for (i_iter = iterms.begin(); i_iter != iterms.end(); ++i_iter) {
1519       odb::dbITerm* cur_i_term = *i_iter;
1520       int x, y;
1521 
1522       if (cur_i_term->getInst()->getPlacementStatus() == odb::dbPlacementStatus::NONE ||
1523           cur_i_term->getInst()->getPlacementStatus() == odb::dbPlacementStatus::UNPLACED) {
1524         x = x_center;
1525         y = y_center;
1526       } else {
1527         cur_i_term->getAvgXY(&x, &y);
1528       }
1529 
1530       inst_pins.push_back(
1531           InstancePin(cur_i_term->getInst()->getConstName(), Point(x, y)));
1532     }
1533 
1534     netlist_.addIONet(io_pin, inst_pins);
1535   }
1536 
1537   int group_idx = 0;
1538   for (PinGroup pin_group : pin_groups_) {
1539     int group_created = netlist_.createIOGroup(pin_group);
1540     if(group_created == pin_group.size()) {
1541       group_idx++;
1542     }
1543   }
1544 }
1545 
commitIOPlacementToDB(std::vector<IOPin> & assignment)1546 void IOPlacer::commitIOPlacementToDB(std::vector<IOPin>& assignment)
1547 {
1548   for (const IOPin& pin : assignment) {
1549     commitIOPinToDB(pin);
1550   }
1551 }
1552 
commitIOPinToDB(const IOPin & pin)1553 void IOPlacer::commitIOPinToDB(const IOPin& pin)
1554 {
1555   odb::dbTechLayer* layer = tech_->findRoutingLayer(pin.getLayer());
1556 
1557   odb::dbBTerm* bterm = block_->findBTerm(pin.getName().c_str());
1558   odb::dbSet<odb::dbBPin> bpins = bterm->getBPins();
1559   odb::dbSet<odb::dbBPin>::iterator bpin_iter;
1560   std::vector<odb::dbBPin*> all_b_pins;
1561   for (bpin_iter = bpins.begin(); bpin_iter != bpins.end(); ++bpin_iter) {
1562     odb::dbBPin* cur_b_pin = *bpin_iter;
1563     all_b_pins.push_back(cur_b_pin);
1564   }
1565 
1566   for (odb::dbBPin* bpin : all_b_pins) {
1567     odb::dbBPin::destroy(bpin);
1568   }
1569 
1570   Point lower_bound = pin.getLowerBound();
1571   Point upper_bound = pin.getUpperBound();
1572 
1573   odb::dbBPin* bpin = odb::dbBPin::create(bterm);
1574 
1575   int x_min = lower_bound.x();
1576   int y_min = lower_bound.y();
1577   int x_max = upper_bound.x();
1578   int y_max = upper_bound.y();
1579 
1580   odb::dbBox::create(bpin, layer, x_min, y_min, x_max, y_max);
1581   bpin->setPlacementStatus(pin.getPlacementStatus());
1582 }
1583 
1584 }  // namespace ppl
1585