Skip to content

SPICE + GDS to pic.yml Conversion

Extends the basic SPICE converter by reading an existing GDS file to extract real layout coordinates. This produces a pic.yml that reflects the actual chip placement rather than an auto-generated grid.

Component positions are matched by extracting layer geometry (e.g. the PolyResdrawing layer for rppd resistors) and correlating dimensions against SPICE parameters.

Requirements:

  • IHP PDK: uv pip install ihp-gdsfactory
import sys
sys.path.insert(0, '.')

from pathlib import Path

from spice_and_gds_to_pic_yml import convert_file

Inputs

We need both the SPICE netlist and the corresponding GDS layout file.

spice_file = Path('TO_Apr2025/Mixer5GHz.spice')
gds_file = Path('TO_Apr2025/FMD_QNC_16_Mixer5GHz.gds')

print('SPICE:', spice_file.exists())
print('GDS:  ', gds_file.exists())
SPICE: True
GDS:   True

Convert with real GDS placements

Passing gds_file causes the converter to extract component bounding boxes from each layer and match them against SPICE parameter dimensions. Matched components use the GDS (x, y) coordinates; unmatched components fall back to a grid layout.

output_file = Path('/tmp/Mixer5GHz_gds.pic.yml')
convert_file(spice_file, output_file, gds_file=gds_file)
Parsing SPICE netlist: TO_Apr2025/Mixer5GHz.spice
Found 30 instances
Extracting rppd placements from GDS: TO_Apr2025/FMD_QNC_16_Mixer5GHz.gds


  Found PolyRes layer by number: 128/0 (index 51)
  Extracted 6 rppd instances
Extracting inductor placements from GDS: TO_Apr2025/FMD_QNC_16_Mixer5GHz.gds


2026-07-06 16:04:10.764 | ERROR    | kfactory.kcell:name:691 - Name conflict in kfactory.kcell::name at line 691
Renaming Unnamed_21 (cell_index=21) to Mixer5GHz would cause it to be named the same as:
 - Mixer5GHz (cell_index=0), function_name=None, basename=None
  Found inductor layer by number: 1/23 (index 72)


  Extracted 4 inductor instances
Extracting NMOS placements from GDS: TO_Apr2025/FMD_QNC_16_Mixer5GHz.gds


2026-07-06 16:04:11.066 | ERROR    | kfactory.kcell:name:691 - Name conflict in kfactory.kcell::name at line 691
Renaming Unnamed_42 (cell_index=42) to Mixer5GHz would cause it to be named the same as:
 - Mixer5GHz (cell_index=0), function_name=None, basename=None
 - Mixer5GHz (cell_index=21), function_name=None, basename=None


  Extracted 16 NMOS instances
Extracting CMIM placements from GDS: TO_Apr2025/FMD_QNC_16_Mixer5GHz.gds


2026-07-06 16:04:11.402 | ERROR    | kfactory.kcell:name:691 - Name conflict in kfactory.kcell::name at line 691
Renaming Unnamed_63 (cell_index=63) to Mixer5GHz would cause it to be named the same as:
 - Mixer5GHz (cell_index=0), function_name=None, basename=None
 - Mixer5GHz (cell_index=21), function_name=None, basename=None
 - Mixer5GHz (cell_index=42), function_name=None, basename=None


  Found MIM layer by number: 36/0 (index 11)


  Extracted 4 CMIM instances

=== SPICE rppd instances (w/l converted to µm) ===
  XR1: dx=4.4000, dy=1.5000
  XR2: dx=4.4000, dy=1.5000
  XR3: dx=4.3500, dy=1.5000
  XR4: dx=4.3500, dy=1.5000
  XRL1: dx=4.5000, dy=3.2000
  XRL2: dx=4.5000, dy=3.2000

=== GDS rppd instances (dx/dy from PolyRes layer) ===
  rppd$1: dx=4.4000, dy=1.5000 @ (631.56, 1066.89)
  rppd$2: dx=4.5000, dy=3.2000 @ (655.43, 1047.70)
  rppd$2: dx=4.5000, dy=3.2000 @ (678.50, 1047.70)
  rppd$1: dx=4.4000, dy=1.5000 @ (622.85, 1066.89)
  rppd: dx=4.3500, dy=1.5000 @ (622.85, 280.99)
  rppd: dx=4.3500, dy=1.5000 @ (631.50, 280.99)

=== Matching ===

  dx=4.35, dy=1.5: 2 SPICE → 2 GDS
    XR3 → (622.85, 280.99)
    XR4 → (631.50, 280.99)

  dx=4.4, dy=1.5: 2 SPICE → 2 GDS
    XR1 → (631.56, 1066.89)
    XR2 → (622.85, 1066.89)

  dx=4.5, dy=3.2: 2 SPICE → 2 GDS
    XRL1 → (655.43, 1047.70)
    XRL2 → (678.50, 1047.70)

✓ Matched 6 of 6 rppd instances

=== Matching Inductors ===
  4 SPICE inductors → 4 GDS inductors
    L1 → (846.02, 1088.35), diameter=342.04
    L2 → (403.98, 1088.35), diameter=342.04
    L3 → (403.98, 302.44), diameter=342.04
    L4 → (846.02, 302.44), diameter=342.04

✓ Matched 4 of 4 inductors

=== Matching NMOS ===
  16 SPICE NMOS → 16 GDS NMOS
    XM6 (nf=10) → (648.81, 1025.98), width=5.40
    XM8 (nf=10) → (657.61, 1025.98), width=5.40
    XM7 (nf=10) → (666.41, 1025.98), width=5.40
    XM5 (nf=10) → (675.21, 1025.98), width=5.40
    XM4 (nf=15) → (629.46, 1012.88), width=7.95
    XM3 (nf=15) → (651.38, 1012.88), width=7.95
    XM11 (nf=15) → (668.98, 1012.88), width=7.95
    XM12 (nf=15) → (611.86, 1012.88), width=7.95
    XM13 (nf=15) → (611.86, 226.97), width=7.95
    XM14 (nf=15) → (629.46, 226.97), width=7.95
    XM2 (nf=20) → (618.93, 998.26), width=10.50
    XM1 (nf=20) → (618.93, 212.36), width=10.50
    XM9 (nf=20) → (631.43, 998.26), width=10.50
    XM10 (nf=20) → (645.93, 998.26), width=10.50
    XM15 (nf=20) → (658.43, 998.26), width=10.50
    XM16 (nf=20) → (631.43, 212.36), width=10.50

✓ Matched 16 of 16 NMOS

=== SPICE cmim instances (w/l converted to µm) ===
  XC1: width=11.7450, length=9.4450
  XC2: width=11.7450, length=9.4450
  XC3: width=19.1000, length=10.7000
  XC4: width=19.1000, length=10.7000

=== GDS cmim instances (width/length from MIM layer) ===
  cmim$1$3: width=11.7450, length=9.4450 @ (637.82, 1061.80)
  cmim$1$3: width=11.7450, length=9.4450 @ (600.45, 1061.80)
  cmim$4: width=19.1000, length=10.7000 @ (591.60, 275.88)
  cmim$4: width=19.1000, length=10.7000 @ (639.31, 275.88)

=== Matching ===

  width=11.745, length=9.445: 2 SPICE → 2 GDS
    XC1 → (637.82, 1061.80)
    XC2 → (600.45, 1061.80)

  width=19.1, length=10.7: 2 SPICE → 2 GDS
    XC3 → (591.60, 275.88)
    XC4 → (639.31, 275.88)

✓ Matched 4 of 4 cmim instances
Converting to pic.yml format...
Writing to /tmp/Mixer5GHz_gds.pic.yml
✓ Conversion complete!

Summary:
  - 30 components
  - 30 placements
  - Used GDS coordinates from: FMD_QNC_16_Mixer5GHz.gds
print(output_file.read_text())
name: circuit
instances:
  XM6:
    component: nmos
    settings:
      width: 60.0
      length: 0.13
      nf: 10
      model: sg13_lv_nmos
  XRL2:
    component: rppd
    settings:
      dx: 4.5
      dy: 3.2
      model: rppd
  XRL1:
    component: rppd
    settings:
      dx: 4.5
      dy: 3.2
      model: rppd
  XM8:
    component: nmos
    settings:
      width: 60.0
      length: 0.13
      nf: 10
      model: sg13_lv_nmos
  XM7:
    component: nmos
    settings:
      width: 60.0
      length: 0.13
      nf: 10
      model: sg13_lv_nmos
  XM5:
    component: nmos
    settings:
      width: 60.0
      length: 0.13
      nf: 10
      model: sg13_lv_nmos
  XM4:
    component: nmos
    settings:
      width: 90.0
      length: 0.13
      nf: 15
      model: sg13_lv_nmos
  XM3:
    component: nmos
    settings:
      width: 90.0
      length: 0.13
      nf: 15
      model: sg13_lv_nmos
  XM2:
    component: nmos
    settings:
      width: 120.0
      length: 0.13
      nf: 20
      model: sg13_lv_nmos
  XM1:
    component: nmos
    settings:
      width: 120.0
      length: 0.13
      nf: 20
      model: sg13_lv_nmos
  XM11:
    component: nmos
    settings:
      width: 90.0
      length: 0.13
      nf: 15
      model: sg13_lv_nmos
  XM12:
    component: nmos
    settings:
      width: 90.0
      length: 0.13
      nf: 15
      model: sg13_lv_nmos
  XM9:
    component: nmos
    settings:
      width: 120.0
      length: 0.13
      nf: 20
      model: sg13_lv_nmos
  XM10:
    component: nmos
    settings:
      width: 120.0
      length: 0.13
      nf: 20
      model: sg13_lv_nmos
  XC1:
    component: cmim
    settings:
      width: 11.745
      length: 9.445
  XR1:
    component: rppd
    settings:
      dx: 4.4
      dy: 1.5
      model: rppd
  XC2:
    component: cmim
    settings:
      width: 11.745
      length: 9.445
  XR2:
    component: rppd
    settings:
      dx: 4.4
      dy: 1.5
      model: rppd
  XM13:
    component: nmos
    settings:
      width: 90.0
      length: 0.13
      nf: 15
      model: sg13_lv_nmos
  XM14:
    component: nmos
    settings:
      width: 90.0
      length: 0.13
      nf: 15
      model: sg13_lv_nmos
  XM15:
    component: nmos
    settings:
      width: 120.0
      length: 0.13
      nf: 20
      model: sg13_lv_nmos
  XM16:
    component: nmos
    settings:
      width: 120.0
      length: 0.13
      nf: 20
      model: sg13_lv_nmos
  XC3:
    component: cmim
    settings:
      width: 19.1
      length: 10.7
  XR3:
    component: rppd
    settings:
      dx: 4.35
      dy: 1.5
      model: rppd
  XC4:
    component: cmim
    settings:
      width: 19.1
      length: 10.7
  XR4:
    component: rppd
    settings:
      dx: 4.35
      dy: 1.5
      model: rppd
  L3:
    component: inductor2
    settings:
      inductance: 2.006e-09
  L1:
    component: inductor2
    settings:
      inductance: 2.006e-09
  L2:
    component: inductor2
    settings:
      inductance: 2.006e-09
  L4:
    component: inductor2
    settings:
      inductance: 2.006e-09
placements:
  XM6:
    x: 648.8100000000001
    y: 1025.98
  XRL2:
    x: 678.495
    y: 1047.705
    rotation: 180.0
  XRL1:
    x: 655.4250000000001
    y: 1047.705
    rotation: 180.0
  XM8:
    x: 657.61
    y: 1025.98
  XM7:
    x: 666.41
    y: 1025.98
  XM5:
    x: 675.21
    y: 1025.98
  XM4:
    x: 629.46
    y: 1012.88
  XM3:
    x: 651.38
    y: 1012.88
  XM2:
    x: 618.9300000000001
    y: 998.265
  XM1:
    x: 618.9300000000001
    y: 212.35500000000002
  XM11:
    x: 668.98
    y: 1012.88
  XM12:
    x: 611.86
    y: 1012.88
  XM9:
    x: 631.4300000000001
    y: 998.265
  XM10:
    x: 645.9300000000001
    y: 998.265
  XC1:
    x: 637.815
    y: 1061.795
  XR1:
    x: 631.5550000000001
    y: 1066.895
    rotation: 180.0
  XC2:
    x: 600.445
    y: 1061.795
  XR2:
    x: 622.845
    y: 1066.895
    rotation: 180.0
  XM13:
    x: 611.86
    y: 226.97
  XM14:
    x: 629.46
    y: 226.97
  XM15:
    x: 658.4300000000001
    y: 998.265
  XM16:
    x: 631.4300000000001
    y: 212.35500000000002
  XC3:
    x: 591.595
    y: 275.885
  XR3:
    x: 622.845
    y: 280.985
    rotation: 180.0
  XC4:
    x: 639.3050000000001
    y: 275.885
  XR4:
    x: 631.505
    y: 280.985
    rotation: 180.0
  L3:
    x: 403.98
    y: 302.435
    rotation: 180.0
    mirror: true
  L1:
    x: 846.02
    y: 1088.345
  L2:
    x: 403.98
    y: 1088.345
    rotation: 180.0
    mirror: true
  L4:
    x: 846.02
    y: 302.435

Next steps

The generated pic.yml preserves real GDS coordinates and can be loaded with gf.read.from_yaml() once all component parameters are within the PDK's accepted ranges. This file is well-suited as a base for schematic-driven layout re-generation.