@@ -238,6 +238,36 @@ static void findStartEnd(int x,
238238 }
239239}
240240
241+ // Subtract the blocker intervals from [start, end), returning the open gaps.
242+ static std::vector<std::pair<int , int >> computeOpenSpans (
243+ const int start,
244+ const int end,
245+ std::vector<std::pair<int , int >> blockers)
246+ {
247+ std::ranges::sort (blockers);
248+
249+ std::vector<std::pair<int , int >> open_spans;
250+ int cursor = start;
251+ for (const auto & [b_start, b_end] : blockers) {
252+ // blockers are sorted by start, so none of the rest can open a span
253+ if (b_start >= end) {
254+ break ;
255+ }
256+ if (b_end <= cursor) {
257+ continue ;
258+ }
259+ if (b_start > cursor) {
260+ open_spans.emplace_back (cursor, b_start);
261+ }
262+ cursor = b_end;
263+ }
264+ if (cursor < end) {
265+ open_spans.emplace_back (cursor, end);
266+ }
267+
268+ return open_spans;
269+ }
270+
241271std::optional<int > Tapcell::findValidLocation (
242272 int x,
243273 int width,
@@ -524,6 +554,8 @@ void Tapcell::placeEndcaps(const EndcapCellOptions& options)
524554 }
525555
526556 filled_edges_.clear ();
557+ filled_horizontal_edges_.clear ();
558+ placed_corners_.clear ();
527559}
528560
529561std::vector<Tapcell::Edge> Tapcell::getBoundaryEdges (const Polygon& area,
@@ -873,18 +905,19 @@ std::pair<int, int> Tapcell::placeEndcaps(const Tapcell::Polygon90& area,
873905 int corner_count = 0 ;
874906 int endcaps = 0 ;
875907
876- CornerMap corners;
877- // insert corners first
908+ // insert corners first. placed_corners_ persists across areas/holes so that
909+ // edges and corners of one macro's hole see the corners already placed by an
910+ // adjacent macro's hole in the same row.
878911 for (const auto & corner : getBoundaryCorners (area, outer)) {
879912 for (const auto & [row, insts] : placeEndcapCorner (corner, options)) {
880- corners [row].insert (insts.begin (), insts.end ());
913+ placed_corners_ [row].insert (insts.begin (), insts.end ());
881914 corner_count += insts.size ();
882915 }
883916 }
884917
885918 for (const auto & edge : getBoundaryEdges (area, outer)) {
886919 if (std::ranges::find (filled_edges_, edge) == filled_edges_.end ()) {
887- endcaps += placeEndcapEdge (edge, corners , options);
920+ endcaps += placeEndcapEdge (edge, placed_corners_ , options);
888921 filled_edges_.push_back (edge);
889922 }
890923 }
@@ -1048,6 +1081,32 @@ Tapcell::CornerMap Tapcell::placeEndcapCorner(const Tapcell::Corner& corner,
10481081 return {};
10491082 }
10501083
1084+ // Skip corners overlapping one already placed in this row, e.g. the inner
1085+ // top and bottom corners of a single-height row between macros.
1086+ auto placed = placed_corners_.find (row);
1087+ if (placed != placed_corners_.end ()) {
1088+ const odb::Rect cell (
1089+ ll.getX (), ll.getY (), ll.getX () + width, ll.getY () + height);
1090+ for (auto * other : placed->second ) {
1091+ if (cell.overlaps (other->getBBox ()->getBox ())) {
1092+ return {};
1093+ }
1094+ }
1095+ }
1096+
1097+ // Skip corners overlapping a horizontal edge already placed in this row: an
1098+ // adjacent macro's hole may have filled the row before this corner.
1099+ auto filled = filled_horizontal_edges_.find (row);
1100+ if (filled != filled_horizontal_edges_.end ()) {
1101+ const int x_start = ll.getX ();
1102+ const int x_end = ll.getX () + width;
1103+ for (const auto & [e_start, e_end] : filled->second ) {
1104+ if (x_end > e_start && x_start < e_end) {
1105+ return {};
1106+ }
1107+ }
1108+ }
1109+
10511110 auto inst = makeInstance (db_->getChip ()->getBlock (),
10521111 master,
10531112 orient,
@@ -1166,59 +1225,93 @@ int Tapcell::placeEndcapEdgeHorizontal(const Tapcell::Edge& edge,
11661225 }
11671226 }
11681227
1169- odb::Point ll = row->getBBox ().ll ();
1170- ll.setX (e0 .getX ());
1228+ // Fill only x-ranges not already covered by another horizontal edge in this
1229+ // row, so a single-height row between macros gets one edge, not overlaps.
1230+ std::vector<std::pair<int , int >>& occupied = filled_horizontal_edges_[row];
11711231
1232+ // Also skip the corner cells in this row that land within the span.
1233+ std::vector<std::pair<int , int >> blockers (occupied);
1234+ if (check_row != corners.end ()) {
1235+ for (auto * inst : check_row->second ) {
1236+ const auto bbox = inst->getBBox ()->getBox ();
1237+ blockers.emplace_back (bbox.xMin (), bbox.xMax ());
1238+ }
1239+ }
1240+
1241+ for (const auto & [span_start, span_end] :
1242+ computeOpenSpans (e0 .getX (), e1 .getX (), std::move (blockers))) {
1243+ insts += fillEndcapEdge (
1244+ row, span_start, span_end, masters, edge.type , options.prefix );
1245+ occupied.emplace_back (span_start, span_end);
1246+ }
1247+
1248+ return insts;
1249+ }
1250+
1251+ int Tapcell::fillEndcapEdge (odb::dbRow* row,
1252+ const int x_start,
1253+ const int x_end,
1254+ const std::vector<odb::dbMaster*>& masters,
1255+ const EdgeType edge_type,
1256+ const std::string& prefix)
1257+ {
1258+ // Consider only masters that can be legally placed in this row's
1259+ // orientation. masters is sorted widest first, so the last valid one is the
1260+ // narrowest, used as a fallback when none divides the span evenly.
11721261 auto pick_next_master
1173- = [&e1 , &masters](const odb::Point& ll) -> odb::dbMaster* {
1174- int remaining = e1 .getX () - ll.getX ();
1262+ = [this , x_end, &masters, row](int x) -> odb::dbMaster* {
1263+ const int remaining = x_end - x;
1264+ odb::dbMaster* fallback = nullptr ;
11751265 for (auto * master : masters) {
1266+ if (!checkSymmetry (master, row->getOrient ())) {
1267+ continue ;
1268+ }
1269+ fallback = master;
11761270 if (remaining % master->getWidth () == 0 ) {
11771271 return master;
11781272 }
11791273 }
1180- // pick smallest if none will divide evenly
1181- return masters[masters.size () - 1 ];
1274+ return fallback;
11821275 };
11831276
1184- while (ll.getX () < e1 .getX ()) {
1185- auto * master = pick_next_master (ll);
1186-
1187- debugPrint (logger_,
1188- utl::TAP ,
1189- " Endcap" ,
1190- 3 ,
1191- " From {} -> {}: picked {}" ,
1192- ll.getX (),
1193- e1 .getX (),
1194- master->getName ());
1195-
1196- if (!checkSymmetry (master, row->getOrient ())) {
1197- continue ;
1198- }
1277+ const int row_lly = row->getBBox ().yMin ();
1278+ int insts = 0 ;
1279+ int x = x_start;
1280+ while (x < x_end) {
1281+ auto * master = pick_next_master (x);
11991282
1200- if (ll.getX () + master->getWidth () > e1 .getX ()) {
1283+ // No symmetric master fits the remaining space: the boundary cannot be
1284+ // filled without leaving a gap.
1285+ if (master == nullptr || x + master->getWidth () > x_end) {
12011286 const double dbus = row->getBlock ()->getDbUnitsPerMicron ();
12021287 logger_->error (
12031288 utl::TAP ,
12041289 20 ,
12051290 " Unable to fill {} boundary in {} from {:.4f}um to {:.4f}um" ,
1206- toString (edge. type ),
1291+ toString (edge_type ),
12071292 row->getName (),
1208- ll. getX () / dbus,
1209- e1 . getX () / dbus);
1293+ x / dbus,
1294+ x_end / dbus);
12101295 }
12111296
1212- makeInstance (db_->getChip ()->getBlock (),
1213- master,
1214- row->getOrient (),
1215- ll.getX (),
1216- ll.getY (),
1217- fmt::format (" {}EDGE_{}_{}_" ,
1218- options.prefix ,
1219- row->getName (),
1220- toString (edge.type )));
1221- ll.addX (master->getWidth ());
1297+ debugPrint (logger_,
1298+ utl::TAP ,
1299+ " Endcap" ,
1300+ 3 ,
1301+ " From {} -> {}: picked {}" ,
1302+ x,
1303+ x_end,
1304+ master->getName ());
1305+
1306+ makeInstance (
1307+ db_->getChip ()->getBlock (),
1308+ master,
1309+ row->getOrient (),
1310+ x,
1311+ row_lly,
1312+ fmt::format (
1313+ " {}EDGE_{}_{}_" , prefix, row->getName (), toString (edge_type)));
1314+ x += master->getWidth ();
12221315 insts++;
12231316 }
12241317
0 commit comments