Design Rule Check¶
After finalizing the physical layout of the 800G transceiver chip, the Design Rule Check (DRC) serves as the critical bridge between digital design and physical manufacturing. Even a visually perfect layout can contain microscopic violations—such as traces that are too narrow or elements placed too close together—that would lead to total device failure during the lithography and etching processes at the foundry.
This notebook provides an automated workflow to validate the 800G_2xFR4.gds layout against the SiEPIC_EBeam process requirements. By integrating the KLayout DRC engine directly with PhotonForge, we can programmatically identify “un-manufacturable” geometries, generate visual error markers, and iteratively refine the design.
The following sections guide you through environment setup, batch execution of the DRC deck, and the post-processing steps required to visualize violations on a dedicated error layer for quick debugging.
First we build the chip layout and export it to an GDS file.
[1]:
%run Chip_Layout.ipynb
pic.write_gds()
LiveViewer started at http://localhost:33233
Starting…
12:03:24 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-1aba0c07-e680- 4ff2-8036-430d946b98fe'.
Progress: 100%
Starting…
12:03:25 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-17d97adb-a155- 47d1-8bd4-8cb2dec096af'.
Progress: 100%
Taper angle: 10.282162084410936°
Starting…
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-d4d2571e-687 d-42e8-85a5-9e28938d8c79'.
Progress: 100%
Starting…
12:03:28 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-8829638f-b0f 3-4301-9145-0cc93a074c81'.
12:03:29 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-3174c679-922c- 4dd8-a168-8eca12971ab5'.
Progress: 100%
Starting…
12:03:30 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-eee6c729-12d 3-4b8e-b459-727b7ff6b311'.
Progress: 100%
Starting…
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-6cb5f751-5d9 1-486d-a43b-69556839fb6e'.
Progress: 100%
The internal coupling ratio at the center wavelength is: 0.0201
Starting…
12:03:31 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-37fd088c-5cf c-4aa6-af7a-a855032b506f'.
Progress: 100%
The external coupling ratio at the center wavelength is: 0.203
Starting…
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-37fd088c-5cf c-4aa6-af7a-a855032b506f'.
12:03:32 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-62a43cd1-afb1- 4c4a-ac64-e5bc31ac491c'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-4e89e43e-a529- 4d8d-b18d-c94dbd3b3b01'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-6cb5f751-5d9 1-486d-a43b-69556839fb6e'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-194e05d1-4fbf- 4ebd-a449-28cbe6161fe8'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-0172b8aa-d938- 4d4c-ac0d-4562c9063039'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-40a34fe6-8a7b- 4a10-b722-1996223952a6'.
12:03:33 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-46c11e52-f7aa- 43cd-9bba-37cdad3d1114'.
Progress: 100%
Est. FSR: 11.6 nm
Required added_length: 80.0 nm
Starting…
12:03:34 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-37fd088c-5cf c-4aa6-af7a-a855032b506f'.
12:03:35 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-6cb5f751-5d9 1-486d-a43b-69556839fb6e'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-fa738d51-cd6 3-4f56-b445-4fd2ca2b0dce'.
12:03:36 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-275cd3f4-dba 3-441f-8a58-9f9882928b0a'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-424a5f7a-26f c-44c6-8af5-a9c1313400eb'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-9ba56d49-926 e-4869-9996-46cdf74065c6'.
12:03:37 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-a6fa4e23-613 2-46b6-b01e-e28e581daba6'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-94d55feb-c18 c-4166-87e9-117019c4dfb6'.
Progress: 100%
Progress: 100%
Progress: 100%
Starting…
12:03:39 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-363c83f8-df78- 4614-8cba-dcf0ca950540'.
Progress: 100%
Starting…
12:03:40 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-387e7d8f-9c52- 46bb-b7b7-a2be3377dfda'.
Progress: 100%
Required termination width for 100 Ohms: 12.50 µm
Starting…
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-8e1f0a67-9f8 5-4600-bee1-a2fe2f37f314'.
12:03:41 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-67772b07-d09 6-49cd-9f44-10b2cc57b1c1'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-3e05a0f7-594 a-440e-b88b-d58af5edff83'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-cab67f0a-2123- 4baa-ba40-14bbeee814c5'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-2b697720-500e- 4ff6-9fc8-32ddd7540b08'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-da0b3c2b-49a7- 4b90-a5dc-cbb69f6d9eab'.
Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=mo-867b237c-8dcd- 4b77-b859-5411dc923682'.
Progress: 100%
Progress: 100%
Progress: 100%
Starting…
12:03:44 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-51a67ba0-db1 2-4146-a8f4-f2b0f2be3eb7'.
Progress: 100%
Starting…
12:03:46 EDT Loading simulation from local cache. View cached task using web UI at 'https://tidy3d.simulation.cloud/workbench?taskId=fdve-ebf75b07-a19 1-40d8-ab20-d209ddbf8640'.
Progress: 100%
[1]:
Setup KLayout and Environment¶
We initialize the DRCRunner and DRCResults plugins from Tidy3D. This setup allows us to bridge the gap between layout generation and foundry-specific rule validation without leaving the Jupyter interface. For more information see this guide.
[2]:
from tidy3d.plugins.klayout.drc import DRCResults, DRCRunner
td.config.logging_level = "INFO"
File Paths and Technology¶
In this section, we define the paths for the input GDS, the DRC runset (rule deck), and the output database (.lyrdb). These files ensure that the check is performed against the correct lithography constraints for the SiEPIC EBeam process.
[3]:
from pathlib import Path
# File paths (relative to notebook location)
current_dir = Path.cwd()
gds_file = current_dir / "800G_2xFR4.gds"
drc_runset = current_dir / "SiEPIC_EBeam.drc"
results_file = current_dir / "drc_results.lyrdb"
print(f"GDS file: {gds_file}")
print(f"DRC runset: {drc_runset}")
print(f"Results file: {results_file}")
GDS file: /home/amin/photonforge-docs/docs/examples/800G_2xFR4.gds
DRC runset: /home/amin/photonforge-docs/docs/examples/SiEPIC_EBeam.drc
Results file: /home/amin/photonforge-docs/docs/examples/drc_results.lyrdb
Load GDS Layout¶
The layout is loaded into the PhotonForge environment. This step confirms that the geometry was written correctly to the GDS file and allows us to manipulate the component object for later visualization of error markers.
[4]:
print("Loading GDS layout...")
components = pf.load_layout(str(gds_file))
print(f" Found {len(components)} components")
# Get the top-level component (named after the GDS file)
top_name = gds_file.stem # "800G_2xFR4"
if top_name in components:
chip = components[top_name]
else:
chip = list(components.values())[-1]
print(f"✓ Loaded component: {chip.name}")
print(f" Bounds: {chip.bounds()}")
Loading GDS layout...
Found 220 components
✓ Loaded component: 800G_2xFR4
Bounds: (array([-200., -200.]), array([5700., 5700.]))
Run KLayout DRC¶
We trigger KLayout in “batch mode” to process the geometry. The engine compares every polygon in the design against the rules defined in the .drc file, such as minimum widths, spacing, and enclosure requirements.
[5]:
import subprocess
print("Running DRC...")
# Build KLayout command
klayout_cmd = [
"klayout",
"-b",
"-r",
str(drc_runset),
"-rd",
f"gdsfile={str(gds_file)}",
"-rd",
f"resultsfile={str(results_file)}",
]
print(f" Command: {' '.join(klayout_cmd)}")
result = subprocess.run(klayout_cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"KLayout stderr: {result.stderr}")
else:
print("✓ KLayout DRC completed successfully")
Running DRC...
Command: klayout -b -r /home/amin/photonforge-docs/docs/examples/SiEPIC_EBeam.drc -rd gdsfile=/home/amin/photonforge-docs/docs/examples/800G_2xFR4.gds -rd resultsfile=/home/amin/photonforge-docs/docs/examples/drc_results.lyrdb
✓ KLayout DRC completed successfully
Parse DRC Results¶
The raw XML results from the DRC engine are parsed to provide a readable summary. This allows us to quickly identify which layers or rules (e.g., Metal 2) are causing issues before diving into the physical layout.
[6]:
import re
import xml.etree.ElementTree as ET
# Parse the lyrdb file to extract violations
violations = {}
total_violations = 0
try:
tree = ET.parse(str(results_file))
root = tree.getroot()
# Find all categories
for category in root.findall(".//category"):
cat_name = (
category.find("name").text
if category.find("name") is not None
else "Unknown"
)
violations[cat_name] = []
# Extract violation locations
for item in root.findall(".//item"):
cat_elem = item.find("category")
cat_name = cat_elem.text if cat_elem is not None else "Unknown"
for value in item.findall(".//value"):
if value.text:
violations.setdefault(cat_name, []).append(value.text)
total_violations += 1
# Print summary
print("=" * 60)
print("DRC Results Summary")
print("=" * 60)
if total_violations == 0:
print("✓ DRC PASSED - No violations found!")
else:
print(f"✗ DRC FAILED - {total_violations} violations found:\n")
for cat, items in violations.items():
if items:
print(f" {cat}: {len(items)} violations")
except Exception as e:
print(f"Warning: Could not parse results file: {e}")
violations = {}
============================================================
DRC Results Summary
============================================================
✗ DRC FAILED - 149 violations found:
M2_width: 96 violations
M2_space: 53 violations
Add Violation Markers to Layout¶
To make the errors actionable, we programmatically add polygon markers to an “Errors” layer. This converts abstract coordinate data from the DRC report into visible shapes that overlay the problematic areas of the design.
Marker display limit: For readability, this notebook plots only the first 10 violations per category. The full violation list is still available in the DRC report; this limit only affects on-layout visualization.
[7]:
print("Adding violation markers to layout...")
chip_with_markers = chip
total_markers = 0
coord_pattern = re.compile(r"([0-9.-]+),([0-9.-]+)")
max_markers_per_category = 10 # readability cap: show only first N violations per category
for cat_name, items in violations.items():
if items:
print(f" Processing {len(items)} violations for '{cat_name}'")
for i, value in enumerate(items[:max_markers_per_category]):
coords = None
if value.startswith("edge-pair: ") or value.startswith("polygon: "):
matches = coord_pattern.findall(value)
coords = np.array([(float(c[0]), float(c[1])) for c in matches])
# Create marker
try:
marker_polygon = pf.Polygon(coords)
except RuntimeError:
min_coord = (
np.array((coords[:, 0].min(), coords[:, 1].min()))
- pf.config.grid
)
max_coord = (
np.array((coords[:, 0].max(), coords[:, 1].max()))
+ pf.config.grid
)
marker_polygon = pf.Rectangle(min_coord, max_coord)
chip_with_markers.add("Errors", marker_polygon)
# Add label
center = sum(marker_polygon.bounds()) / 2
short_name = cat_name[:10]
label = pf.Label(
text=f"{short_name}", origin=center, anchor="o", scaling=2.0
)
chip_with_markers.add("Text", label)
total_markers += 1
print(f"\n✓ Added {total_markers} violation markers")
Adding violation markers to layout...
Processing 96 violations for 'M2_width'
Processing 53 violations for 'M2_space'
✓ Added 20 violation markers
Save GDS with Markers¶
The modified layout, containing both the original design and the error markers, is saved as 800G_2xFR4_with_drc.gds. This file serves as a reference for iterative design improvements.
[8]:
output_gds = current_dir / "800G_2xFR4_with_drc.gds"
chip_with_markers.write_gds(str(output_gds))
print(f"✓ Saved GDS with markers: {output_gds}")
✓ Saved GDS with markers: /home/amin/photonforge-docs/docs/examples/800G_2xFR4_with_drc.gds
Visualize and Resolve Violations¶
We use the LiveViewer to inspect the violations in real-time. By inspecting the chip_with_markers object, we can pinpoint the exact locations of failures.
In the final iteration, we identified significant violations related to ‘M2_width’ and ‘M2_space’. To resolve these, the trace width was increased to 10.0, ensuring the metal traces meet the foundry’s minimum manufacturing requirements. After applying this fix, the DRC runner confirms the layout is clean.
[12]:
from photonforge.live_viewer import LiveViewer
viewer = LiveViewer()
viewer(chip_with_markers)
LiveViewer started at http://localhost:35067
[12]:
[10]:
pic.update(trace_width=10.0)
pic.write_gds()
runner = DRCRunner(drc_runset=drc_runset, verbose=True)
results = runner.run(source=gds_file)
if results.is_clean:
print("DRC passed!\n")
else:
print("DRC did not pass!\n")
12:03:53 EDT Running KLayout DRC on GDS file '/home/amin/photonforge-docs/docs/examples/800G_2xFR4.gds' with runset '/home/amin/photonforge-docs/docs/examples/SiEPIC_EBeam.drc' and saving results to 'drc_results.lyrdb'...
12:03:55 EDT KLayout DRC completed successfully.
DRC passed!
[11]:
viewer(pic)
[11]:
Notes¶
Violation markers are shown on the Errors layer (999)
Labels show the violation category (first 10 characters)
Zoom in on the LiveViewer to see individual violations
The GDS with markers is saved to
800G_2xFR4_with_drc.gds
After we run DRC we might need to adjust some of the dimensions and iterate on the design to make sure it is manufacturable.