@@ -902,6 +902,9 @@ def create_simulation_layers(self):
902902 if part .visualise :
903903 self .visualise_region (part .region , part .name , f"part_reg_{ part .name } " )
904904
905+ # Finally, visualise all ports
906+ self .visualise_ports ()
907+
905908 def produce_layers (self , parts ):
906909 """Finalizes and partitions self.layers.
907910
@@ -1455,3 +1458,77 @@ def visualise_region(
14551458 else :
14561459 for i , p in enumerate (points ):
14571460 self .cell .shapes (visualisation_layer ).insert (pya .DText (f"{ label } _{ i + 1 } " , p .x , p .y ))
1461+
1462+ def visualise_ports (self , edge_port_thickness = 500 ):
1463+ """Visualise all ports in a dedicated 'simulation_ports' layer using get_port_data().
1464+
1465+ Args:
1466+ edge_port_thickness (float): Extra outward extension (µm) for EdgePorts to make them more visible.
1467+ """
1468+ dbu = self .layout .dbu
1469+ port_json = self .get_port_data ()
1470+
1471+ for port in self .ports :
1472+ # Initialize visualise_point at the start of each iteration
1473+ visualise_point = None
1474+
1475+ # Try to find a matching entry in port_json
1476+ port_data_list = [p for p in port_json if p .get ("number" ) == port .number ]
1477+ if not port_data_list :
1478+ logging .warning (f"Port { port .number } not found in get_port_data() output, skipping visualisation." )
1479+ continue
1480+ port_data = port_data_list [0 ]
1481+
1482+ # Label based on port type
1483+ if isinstance (port , EdgePort ):
1484+ label = f"edge_port_{ port .number } "
1485+ elif isinstance (port , InternalPort ):
1486+ label = f"internal_port_{ port .number } "
1487+ else :
1488+ logging .warning (f"Unsupported port type { type (port ).__name__ } for port { port .number } " )
1489+ continue
1490+
1491+ # Construct 2D polygon from port_data["polygon"] (ignoring z coordinate)
1492+ if "polygon" in port_data and port_data ["polygon" ]:
1493+ points_2d = [pya .DPoint (p [0 ], p [1 ]) for p in port_data ["polygon" ]]
1494+
1495+ if isinstance (port , EdgePort ) and edge_port_thickness > 0 :
1496+ direction = None
1497+ if port .signal_location .x == self .box .p1 .x :
1498+ # Port on left border of simulation box
1499+ direction = pya .DPoint (- edge_port_thickness , 0 )
1500+ elif port .signal_location .x == self .box .p2 .x :
1501+ # on right border
1502+ direction = pya .DPoint (edge_port_thickness , 0 )
1503+ elif port .signal_location .y == self .box .p1 .y :
1504+ # on bottom border
1505+ direction = pya .DPoint (0 , - edge_port_thickness )
1506+ elif port .signal_location .y == self .box .p2 .y :
1507+ # on top border
1508+ direction = pya .DPoint (0 , edge_port_thickness )
1509+
1510+ if not direction :
1511+ # draw
1512+ poly = pya .DPolygon (points_2d )
1513+ else :
1514+ # Some points are duplicates when projected to 2D. Ensure you get two different points
1515+ points_2d = list (set (points_2d ))
1516+ p1 = points_2d [0 ]
1517+ p2 = points_2d [1 ]
1518+ # Build a thick polygon from shifted points
1519+ poly = pya .DPolygon (
1520+ [
1521+ p1 ,
1522+ p2 ,
1523+ p2 + direction ,
1524+ p1 + direction ,
1525+ ]
1526+ )
1527+ visualise_point = port .signal_location
1528+ else :
1529+ poly = pya .DPolygon (points_2d )
1530+
1531+ region = pya .Region (poly .to_itype (dbu ))
1532+ self .visualise_region (region , label , "simulation_ports" , visualise_point )
1533+ else :
1534+ logging .warning (f"Port { port .number } has no polygon data in get_port_data(), skipping visualisation." )
0 commit comments