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> §ions)
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> §ions) {
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