@@ -1115,6 +1115,40 @@ void FastRouteCore::getPlanarRoute(odb::dbNet* db_net, GRoute& route)
11151115 }
11161116}
11171117
1118+ void FastRouteCore::convertGridsToSegments (
1119+ const std::vector<GPoint3D>& grids,
1120+ int grid_count,
1121+ std::unordered_set<GSegment, GSegmentHash>& net_segs,
1122+ GRoute& route)
1123+ {
1124+ int lastX = (tile_size_ * (grids[0 ].x + 0.5 )) + x_corner_;
1125+ int lastY = (tile_size_ * (grids[0 ].y + 0.5 )) + y_corner_;
1126+ int lastL = grids[0 ].layer ;
1127+
1128+ for (int i = 1 ; i <= grid_count; i++) {
1129+ const int xreal = (tile_size_ * (grids[i].x + 0.5 )) + x_corner_;
1130+ const int yreal = (tile_size_ * (grids[i].y + 0.5 )) + y_corner_;
1131+ const int currentL = grids[i].layer ;
1132+
1133+ if (lastX == xreal && lastY == yreal && lastL == currentL) {
1134+ continue ;
1135+ }
1136+
1137+ GSegment segment
1138+ = GSegment (lastX, lastY, lastL + 1 , xreal, yreal, currentL + 1 );
1139+ segment.setIs3DRoute (true );
1140+
1141+ if (net_segs.find (segment) == net_segs.end ()) {
1142+ net_segs.insert (segment);
1143+ route.push_back (segment);
1144+ }
1145+
1146+ lastX = xreal;
1147+ lastY = yreal;
1148+ lastL = currentL;
1149+ }
1150+ }
1151+
11181152void FastRouteCore::get3DRoute (odb::dbNet* db_net, GRoute& route)
11191153{
11201154 int netID;
@@ -1206,39 +1240,23 @@ void FastRouteCore::get3DRoute(odb::dbNet* db_net, GRoute& route)
12061240 }
12071241 }
12081242
1209- int lastX = (tile_size_ * (filled_grids[0 ].x + 0.5 )) + x_corner_;
1210- int lastY = (tile_size_ * (filled_grids[0 ].y + 0.5 )) + y_corner_;
1211- int lastL = filled_grids[0 ].layer ;
1212-
1213- for (int i = 1 ; i < filled_grids.size (); i++) {
1214- const int xreal = (tile_size_ * (filled_grids[i].x + 0.5 )) + x_corner_;
1215- const int yreal = (tile_size_ * (filled_grids[i].y + 0.5 )) + y_corner_;
1216- const int currentL = filled_grids[i].layer ;
1217-
1218- // Prevent adding segments that are effectively zero-length vias on the
1219- // same layer
1220- if (lastX == xreal && lastY == yreal && lastL == currentL) {
1221- // Skip this segment as it's a redundant via on the same layer
1222- lastX = xreal;
1223- lastY = yreal;
1224- lastL = currentL;
1225- continue ;
1226- }
1227-
1228- GSegment segment
1229- = GSegment (lastX, lastY, lastL + 1 , xreal, yreal, currentL + 1 );
1230- segment.setIs3DRoute (true );
1231-
1232- // Only add segment if it's not a duplicate
1233- if (net_segs.find (segment) == net_segs.end ()) {
1234- net_segs.insert (segment);
1235- route.push_back (segment);
1236- }
1237-
1238- lastX = xreal;
1239- lastY = yreal;
1240- lastL = currentL;
1241- }
1243+ convertGridsToSegments (
1244+ filled_grids, filled_grids.size () - 1 , net_segs, route);
1245+ } else if (treeedge->route .routelen > 0 ) {
1246+ // Handle zero-length edges (len == 0) that carry via route grids.
1247+ // These arise in two situations:
1248+ // (a) fillVIA created a via stack for co-located terminal pins on
1249+ // different layers (e.g. two pins in the same gcell on layers 2
1250+ // and 3).
1251+ // (b) ensurePinCoverage appended a new TreeEdge with a via route to
1252+ // reach an uncovered pin.
1253+ // In both cases len is 0 and n1a/n2a are not set by layerAssignment
1254+ // (which only processes len>0 edges), so the grids are converted
1255+ // directly to GSegments without the pin-via-stack logic used above.
1256+ // Without this branch the via is silently dropped, leaving RSZ unable
1257+ // to build a connected tree (RSZ-0074).
1258+ convertGridsToSegments (
1259+ treeedge->route .grids , treeedge->route .routelen , net_segs, route);
12421260 }
12431261 }
12441262}
0 commit comments