Array instances¶
File: pics/simple_array.pic.yml
Error caught: short + net + open — two routes connecting pad arrays cross each other, causing a geometric short, a missing-net group derived from that short, and dangling ports on the pads that aren't routed.
Expected: ok=False, error_count=9 (2 shorts + 1 missing_in_schematic net group + 6 opens for unrouted pads)
import tempfile
from copy import deepcopy
from pathlib import Path
import elvis
import gdsfactory as gf
from IPython.display import Markdown, display
from kwasm import Tool, show
gf.gpdk.PDK.activate()
PICS = Path("../pics")
BUILD_GDS = Path("../build/gds")
BUILD_GDS.mkdir(parents=True, exist_ok=True)
PIC = PICS / "simple_array.pic.yml"
Schematic¶
Two rows of 5 pads each, 200 µm column pitch. Routes connect selected ports across the two arrays — but they cross each other, causing a short.
{'simple_array': {'instances': {'pads': {'component': 'pad',
'settings': {},
'array': {'columns': 5,
'rows': 1,
'column_pitch': 200.0,
'row_pitch': 100.0}},
'pads2': {'component': 'pad',
'settings': {},
'array': {'columns': 5,
'rows': 1,
'column_pitch': 200.0,
'row_pitch': 100.0}}},
'routes': {'elec1': {'links': {'pads<1.0>,e4': 'pads2<3.0>,e2'},
'routing_strategy': 'route_bundle_electrical',
'settings': {'allow_layer_mismatch': False,
'allow_type_mismatch': False,
'allow_width_mismatch': False}},
'elec2': {'links': {'pads<3.0>,e4': 'pads2<0.0>,e2'},
'routing_strategy': 'route_bundle_electrical',
'settings': {'allow_layer_mismatch': False,
'allow_type_mismatch': False,
'allow_width_mismatch': False}}},
'ports': {},
'placements': {'pads': {'x': 0, 'y': 0, 'dx': -96.428, 'dy': -26.49},
'pads2': {'x': -96.4280014038086,
'y': -26.489999771118164,
'dx': -0.0,
'dy': -515.322,
'rotation': 0,
'mirror': False}}}}
Build from schematic¶
gds_path = BUILD_GDS / "simple_array.gds"
c = gf.read.from_yaml(schematic["simple_array"])
c.write_gds(gds_path)
show(
gds_path,
netlist=PIC,
tools=[Tool.RULER, Tool.CLEAR_ALL, Tool.TOP_PORTS, Tool.INSTANCE_PORTS],
)
Tip: Use
instance-portsto see all 20 port positions (5 pads × 4 ports × 2 arrays). The ruler can verify the 200µm column pitch.
LVS Results¶
rdb = elvis.lvs_rdb(
gds_path,
schematic,
short_layers=[(49, 0)],
equivalent_ports={"pad": ["pad", "e1", "e2", "e3", "e4"]},
)
lyrdb = Path(tempfile.gettempdir()) / "simple_array.lyrdb"
rdb.save(str(lyrdb))
show(
gds_path,
lyrdb=lyrdb,
netlist=PIC,
tools=[Tool.SELECT, Tool.RULER, Tool.CLEAR_ALL],
)
flags used
The above LVS function is called with the following flags
- short_layers=[(49, 0)]
- equivalent_ports={'pad': ["pad", "e1", "e2", "e3", "e4"]}
this can also be pre-configured.
Errors in detail:
print(f"ok={rdb.num_items() == 0}, error_count={rdb.num_items()}")
Markdown(elvis.error_summary(rdb))
ok=False, error_count=9
| cell | error type | description |
|---|---|---|
| simple_array | LVS.net.missing_in_schematic | Net {pads<1.0>,{pad,e1,e2,e3,e4}; pads<3.0>,{pad,e1,e2,e3,e4}; pads2<0.0>,{pad,e1,e2,e3,e4}; pads2<3.0>,{pad,e1,e2,e3,e4}} in layout but not in schematic |
| simple_array | LVS.open | Open port: pads<2.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads2<1.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads<0.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads2<2.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads<4.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads2<4.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.short | Geometric short between 'pads<1.0>,e4 -> pads2<3.0>,e2' and 'pads<3.0>,e4 -> ...' (1 location) |
| simple_array | LVS.short | Geometric short between 'pads<1.0>,e4 -> pads2<3.0>,e2' and 'pads2<0.0>,e2 -> ...' (1 location) |
Fix¶
Uncross the routes in simple_array.pic.yml — swap the right-hand sides of the two links so each route's source and destination columns line up in the same order on both arrays:
routes:
elec1:
links:
pads<1.0>,e4: pads2<0.0>,e2 # was pads2<3.0>,e2
elec2:
links:
pads<3.0>,e4: pads2<3.0>,e2 # was pads2<0.0>,e2
Below we deepcopy the schematic, apply the swap, and re-run LVS:
schematic_fixed = deepcopy(schematic)
routes = schematic_fixed["simple_array"]["routes"]
# Swap the right-hand sides of the two routes
elec1_target = routes["elec1"]["links"]["pads<1.0>,e4"]
elec2_target = routes["elec2"]["links"]["pads<3.0>,e4"]
routes["elec1"]["links"]["pads<1.0>,e4"] = elec2_target
routes["elec2"]["links"]["pads<3.0>,e4"] = elec1_target
c_fixed = gf.read.from_yaml(schematic_fixed["simple_array"])
c_fixed.write_gds(gds_path)
rdb_fixed = elvis.lvs_rdb(
gds_path,
schematic_fixed,
short_layers=[(49, 0)],
equivalent_ports={"pad": ["pad", "e1", "e2", "e3", "e4"]},
)
rdb_fixed.save(str(lyrdb))
display(show(gds_path, lyrdb=lyrdb, netlist=PIC, tools=[Tool.FIT_ALL]))
print(f"ok={rdb_fixed.num_items() == 0}, error_count={rdb_fixed.num_items()}")
Markdown(elvis.error_summary(rdb_fixed))
ok=False, error_count=6
| cell | error type | description |
|---|---|---|
| simple_array | LVS.open | Open port: pads2<4.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads2<2.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads2<1.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads<0.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads<4.0>,{pad,e1,e2,e3,e4} is not connected |
| simple_array | LVS.open | Open port: pads<2.0>,{pad,e1,e2,e3,e4} is not connected |
The 2 shorts and the merged LVS.net.missing_in_schematic group are gone — the topology now matches what the schematic declared. The 6 remaining LVS.open errors are simply the unrouted pads (out of 10 total, only 4 are wired up in this demo); they're orthogonal to the crossing-routes bug and would be there even without the fix.
Why it matters¶
Crossed routes in pad arrays create shorts that are nearly impossible to spot in a layout viewer — the X-shaped crossing looks intentional. Elvis's AREF expansion unpacks the array into individual pad instances and then checks each route chain for polygon overlap, finding the hidden cross-wiring before it causes a circuit failure.