Design Rule Check

008ecc71a6d14633ac6a2f136bc54ecd

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()
../_images/examples_DRC_800G_2_0.png
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%
../_images/examples_DRC_800G_2_4.png
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%
../_images/examples_DRC_800G_2_8.png
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%
../_images/examples_DRC_800G_2_12.png
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%
../_images/examples_DRC_800G_2_17.png
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%
../_images/examples_DRC_800G_2_21.png
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
../_images/examples_DRC_800G_2_25.png
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
../_images/examples_DRC_800G_2_29.png
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%
../_images/examples_DRC_800G_2_40.png
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%
../_images/examples_DRC_800G_2_51.png
Progress: 100%
Progress: 100%
../_images/examples_DRC_800G_2_53.png
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%
../_images/examples_DRC_800G_2_57.png
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%
../_images/examples_DRC_800G_2_61.png
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%
../_images/examples_DRC_800G_2_71.png
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%
../_images/examples_DRC_800G_2_75.png
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%
../_images/examples_DRC_800G_2_79.png
[1]:
../_images/examples_DRC_800G_2_80.svg

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]:
../_images/examples_DRC_800G_18_1.svg
[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]:
../_images/examples_DRC_800G_20_0.svg

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.