Push new pex content

This commit is contained in:
PhillipRambo 2026-02-09 16:01:28 +01:00
parent 91e3f51ad6
commit d3bfeee796
33 changed files with 1051 additions and 2484 deletions

View File

@ -0,0 +1,50 @@
v {xschem version=3.4.6 file_version=1.2}
G {}
K {}
V {}
S {}
E {}
N 330 -280 370 -280 {lab=Vin}
N 330 -280 330 -190 {lab=Vin}
N 330 -190 370 -190 {lab=Vin}
N 410 -230 490 -230 {lab=Vout}
N 410 -230 410 -220 {lab=Vout}
N 410 -250 410 -230 {lab=Vout}
N 410 -160 410 -130 {lab=Gnd}
N 410 -340 410 -310 {lab=Vdd}
N 410 -280 520 -280 {lab=#net1}
N 410 -340 520 -340 {lab=Vdd}
N 410 -190 520 -190 {lab=#net2}
N 410 -130 520 -130 {lab=Gnd}
C {sg13g2_pr/sg13_lv_nmos.sym} 390 -190 0 0 {name=M1
l=0.45u
w=1.0u
ng=1
m=1
model=sg13_lv_nmos
spiceprefix=X
}
C {sg13g2_pr/sg13_lv_pmos.sym} 390 -280 0 0 {name=M2
l=0.45u
w=2.0u
ng=1
m=1
model=sg13_lv_pmos
spiceprefix=X
}
C {iopin.sym} 490 -230 2 1 {name=p2 lab=Vout}
C {iopin.sym} 410 -340 2 0 {name=p5 lab=Vdd}
C {iopin.sym} 330 -240 2 0 {name=p6 lab=Vin}
C {iopin.sym} 410 -130 2 0 {name=p1 lab=Gnd}
C {sg13g2_pr/ntap1.sym} 520 -310 0 0 {name=R1
model=ntap1
spiceprefix=X
w=0.78e-6
l=0.78e-6
}
C {sg13g2_pr/ptap1.sym} 520 -160 2 1 {name=R2
model=ptap1
spiceprefix=X
w=0.78e-6
l=0.78e-6
}

View File

@ -0,0 +1,26 @@
v {xschem version=3.4.6 file_version=1.2}
G {}
K {type=subcircuit
format="@name @pinlist @symname"
template="name=x1"}
V {}
S {}
E {}
L 7 -70 -80 -70 -60 {}
L 7 -150 0 -130 0 {}
L 7 110 0 130 0 {}
L 7 -70 70 -70 90 {}
B 5 -72.5 -82.5 -67.5 -77.5 {name=Vdd dir=inout}
B 5 -152.5 -2.5 -147.5 2.5 {name=Vin dir=inout}
B 5 127.5 -2.5 132.5 2.5 {name=Vout dir=inout}
B 5 -72.5 87.5 -67.5 92.5 {name=Gnd dir=inout}
A 4 105 0 7.071067811865476 135 360 {}
P 4 5 100 0 -130 -80 -130 90 100 0 100 0 {}
T {@symname
} -84 -6 0 0 0.3 0.3 {}
T {@name} -45 -32 0 0 0.2 0.2 {}
T {Vdd} -74 -55 3 1 0.2 0.2 {}
T {Vin} -125 -4 0 0 0.2 0.2 {}
T {Vout} 80 -9 0 1 0.2 0.2 {}
T {Gnd} -66 65 1 1 0.2 0.2 {}

View File

@ -0,0 +1,87 @@
v {xschem version=3.4.6 file_version=1.2}
G {}
K {}
V {}
S {}
E {}
B 2 710 -550 1510 -150 {flags=graph
y1=-0.0023
y2=1.3
ypos1=0
ypos2=2
divy=5
subdivy=1
unity=1
x1=0
x2=2e-06
divx=5
subdivx=1
xlabmag=1.0
ylabmag=1.0
node=vout
color=4
dataset=-1
unitx=1
logx=0
logy=0
}
N 150 -170 150 -140 {lab=Vin}
N 70 -170 70 -140 {lab=Vdd}
N 70 -80 70 -60 {lab=GND}
N 110 -60 150 -60 {lab=GND}
N 150 -80 150 -60 {lab=GND}
N 110 -60 110 -50 {lab=GND}
N 70 -60 110 -60 {lab=GND}
N 320 -410 320 -380 {lab=Vdd}
N 320 -210 320 -190 {lab=GND}
N 220 -300 240 -300 {lab=Vin}
N 520 -300 540 -300 {lab=Vout}
C {vsource.sym} 150 -110 0 0 {name=V1 value="PULSE(0 1.2 0.5u 10n 10n 1u 2u 1)" savecurrent=false}
C {vsource.sym} 70 -110 0 0 {name=V2 value=1.2 savecurrent=false}
C {gnd.sym} 110 -50 0 0 {name=l2 lab=GND}
C {lab_pin.sym} 150 -170 0 0 {name=p1 sig_type=std_logic lab=Vin}
C {lab_pin.sym} 70 -170 0 0 {name=p3 sig_type=std_logic lab=Vdd}
C {code_shown.sym} 40 -540 0 0 {name=NGSPICE
only_toplevel=true
value="
.control
save all
tran 50n 2u
write test_inverter.raw
.endc
" }
C {devices/code_shown.sym} 280 -540 0 0 {name=MODEL only_toplevel=true
format="tcleval( @value )"
value="
.lib cornerMOSlv.lib mos_tt
.lib cornerRES.lib res_typ
"}
C {launcher.sym} 770 -120 0 0 {name=h5
descr="load waves"
tclcommand="xschem raw_read $netlist_dir/test_inverter.raw tran"
}
C {inverter.sym} 390 -300 0 0 {name=x1}
C {lab_pin.sym} 220 -300 0 0 {name=p2 sig_type=std_logic lab=Vin}
C {gnd.sym} 320 -190 0 0 {name=l1 lab=GND}
C {lab_pin.sym} 320 -410 0 0 {name=p4 sig_type=std_logic lab=Vdd}
C {lab_pin.sym} 540 -300 0 1 {name=p5 sig_type=std_logic lab=Vout}
C {inverter.sym} 590 -10 0 0 {name=x2
* NGSPICE file created from inverter.ext - technology: ihp-sg13g2
.subckt inverter_pex Vdd Vin Vout Gnd
X0 Vout Vin Vdd Vdd sg13_lv_pmos ad=0.68p pd=4.68u as=0.68p ps=4.68u w=2u l=0.45u
X1 Vout Vin Gnd Gnd sg13_lv_nmos ad=0.34p pd=2.68u as=0.34p ps=2.68u w=1u l=0.45u
C0 Vout Vin 0.10077f
C1 Vin Vdd 0.14482f
C2 Vout Vdd 0.13155f
R0 Vin Vin.n0 7.52248
C3 Vout Gnd 0.39245f
C4 Vin Gnd 0.64666f
C5 Vdd Gnd 0.15308f
.ends
}

View File

@ -0,0 +1,10 @@
* Extracted by KLayout with SG13G2 LVS runset on : 07/07/2025 16:12
.SUBCKT inverter Gnd Vout Vin Vdd
M$1 Gnd Vin Vout \$1 sg13_lv_nmos L=0.45u W=1u AS=0.34p AD=0.34p PS=2.68u
+ PD=2.68u
M$2 Vdd Vin Vout \$2 sg13_lv_pmos L=0.45u W=2u AS=0.68p AD=0.68p PS=4.68u
+ PD=4.68u
R$3 \$2 Vdd ntap1 A=0.6084p P=3.12u
R$4 \$1 Gnd ptap1 A=0.6084p P=3.12u
.ENDS inverter

View File

@ -0,0 +1,30 @@
#!/bin/bash
set -e
# Hardcoded paths (except PDK stuff)
PYTHON_ENV="/home/pedersen/misc/klayout_pex/bin/activate"
KPEX_MAGIC_EXE="/home/pedersen/.local/bin/magic"
LAYOUT_PATH="../layout/inverter.gds"
SCHEMATIC="../simulations/inverter.spice"
PDK_NAME="ihp_sg13g2"
MAGICRC="$PDK_ROOT/$PDK_NAME/libs.tech/magic/ihp-sg13g2.magicrc"
# Activate Python environment
source "$PYTHON_ENV"
# Run parasitic extraction with kpex
kpex \
--pdk "$PDK_NAME" \
--magic \
--schematic "$SCHEMATIC" \
--gds "$LAYOUT_PATH" \
--magicrc "$MAGICRC" \
--magic_mode RC \
--magic_cthresh 0.02 \
--magic_rthresh 50 \
--magic_short resistor \
--magic_merge conservative \
--out_dir ./pex_output

View File

@ -0,0 +1,23 @@
timestamp 0
version 8.3
tech ihp-sg13g2
style ngspice()
scale 1000 1 0.5
resistclasses 3000000 67000 110 88 88 88 88 18 11
parameters sg13_lv_nmos l=l w=w a1=as p1=ps a2=ad p2=pd
parameters sg13_lv_pmos l=l w=w a1=as p1=ps a2=ad p2=pd
port "Vout" 2 266 -211 298 -173 m1
port "Vin" 3 95 -213 127 -175 m1
port "Vdd" 5 -205 318 -173 356 m1
port "Gnd" 4 20 -732 52 -694 m1
node "ptap1_0.sub!" 0 0 28 -796 isosub 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
node "Vout" 1 392.446 266 -211 m1 0 0 0 0 91916 2354 0 0 0 0 0 0 0 0 0 0 0 0
node "Vin" 0 646.659 95 -213 m1 0 0 0 0 13590 482 0 0 0 0 0 0 0 0 0 0 0 0
node "Vdd" 1 153.076 -205 318 m1 0 0 0 0 42800 1364 0 0 0 0 0 0 0 0 0 0 0 0
equiv "Vdd" "ntap1_0.well"
substrate "Gnd" 0 0 20 -732 m1 0 0 0 0 21536 1036 0 0 0 0 0 0 0 0 0 0 0 0
cap "Vdd" "Vin" 144.819
cap "Vdd" "Vout" 131.55
cap "Vout" "Vin" 100.772
device msubckt sg13_lv_nmos 67 -562 68 -561 l=90 w=200 "Gnd" "Vin" 180 0 "Gnd" 200 13600,536 "Vout" 200 13600,536
device msubckt sg13_lv_pmos 67 9 68 10 l=90 w=400 "Vdd" "Vin" 180 0 "Vdd" 400 27200,936 "Vout" 400 27200,936

View File

@ -0,0 +1,28 @@
scale 1000 1 0.5
rnode "Gnd.t1" 0 0 29 -462 0
rnode "Gnd.t0" 0 0 112 -462 0
rnode "Gnd.n0" 0 0 62 -754 0
rnode "Gnd" 0 0 20 -732 0
resist "Gnd" "Gnd.n0" 0.0257609
resist "Gnd.n0" "Gnd.t1" 6.29433
resist "Gnd.n0" "Gnd.t0" 1079.2
rnode "Vout.t1" 0 0 195 311 0
rnode "Vout.t0" 0 0 195 -462 0
rnode "Vout" 0 0 266 -211 0
resist "Vout" "Vout.t1" 3.28762
resist "Vout" "Vout.t0" 6.19274
rnode "Vdd" 0 0 -205 318 0
rnode "Vdd.t0" 0 0 29 379 0
rnode "Vdd.n0" 0 0 -153 379 0
rnode "Vdd.n1" 0 0 -153 379 0
rnode "ntap1_0.well" 0 0 112 209 0
resist "Vdd.n0" "Vdd" 0.0476667
resist "Vdd.n0" "Vdd.t0" 3.15328
resist "ntap1_0.well" "Vdd.n1" 4.25
resist "Vdd.n1" "Vdd.n0" 8.5
resist "Vdd.n1" "Vdd" 8.51467
rnode "Vin.t0" 0 0 111 -158 0
rnode "Vin" 0 0 95 -213 0
resist "Vin" "Vin.t0" 7.52198
device msubckt sg13_lv_nmos 67 -562 68 -561 "Gnd.t0" "Vin.t0" 180 0 "Gnd.t1" 200 13600,536 "Vout.t0" 200 13600,536
device msubckt sg13_lv_pmos 67 9 68 10 "ntap1_0.well" "Vin.t0" 180 0 "Vdd.t0" 400 27200,936 "Vout.t1" 400 27200,936

View File

@ -0,0 +1,25 @@
# Generated by kpex 0.3.7
crashbackups stop
drc off
gds read /home/pedersen/misc/inverter_example/layout/inverter.gds
load inverter
select top cell
flatten inverter_flat
load inverter_flat
cellname delete inverter -noprompt
cellname rename inverter_flat inverter
select top cell
extract path /home/pedersen/misc/inverter_example/pex/pex_output/inverter__inverter/magic_RC
extract all
ext2sim labels on
ext2sim -p /home/pedersen/misc/inverter_example/pex/pex_output/inverter__inverter/magic_RC
extresist tolerance 1
extresist all
ext2spice short resistor
ext2spice merge conservative
ext2spice cthresh 0.02
ext2spice extresist on
ext2spice subcircuits top on
ext2spice format ngspice
ext2spice -p /home/pedersen/misc/inverter_example/pex/pex_output/inverter__inverter/magic_RC -o /home/pedersen/misc/inverter_example/pex/pex_output/inverter__inverter/magic_RC/inverter.pex.spice
quit -noprompt

View File

@ -0,0 +1,24 @@
timestamp 0
version 8.3
tech ihp-sg13g2
style ngspice()
scale 1000 1 0.5
resistclasses 3000000 67000 110 88 88 88 88 18 11
parameters sg13_lv_nmos l=l w=w a1=as p1=ps a2=ad p2=pd
parameters sg13_lv_pmos l=l w=w a1=as p1=ps a2=ad p2=pd
port "Vout" 3 266 -211 298 -173 m1
port "Vin" 4 95 -213 127 -175 m1
port "Vdd" 6 -237 285 -137 405 m1
port "well" 2 -265 267 -109 423 nw
port "Gnd" 5 -22 -770 78 -670 m1
node "sub!" 0 0 28 -796 isosub 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
node "Vout" 1 392.446 266 -211 m1 0 0 0 0 91916 2354 0 0 0 0 0 0 0 0 0 0 0 0
node "Vin" 0 646.659 95 -213 m1 0 0 0 0 13590 482 0 0 0 0 0 0 0 0 0 0 0 0
node "Vdd" 1 153.076 -237 285 m1 0 0 0 0 42800 1364 0 0 0 0 0 0 0 0 0 0 0 0
equiv "Vdd" "well"
substrate "Gnd" 0 0 -22 -770 m1 0 0 0 0 21536 1036 0 0 0 0 0 0 0 0 0 0 0 0
cap "Vout" "Vdd" 131.55
cap "Vout" "Vin" 100.772
cap "Vdd" "Vin" 144.819
device msubckt sg13_lv_nmos 67 -562 68 -561 l=90 w=200 "Gnd" "Vin" 180 0 "Gnd" 200 13600,536 "Vout" 200 13600,536
device msubckt sg13_lv_pmos 67 9 68 10 l=90 w=400 "Vdd" "Vin" 180 0 "Vdd" 400 27200,936 "Vout" 400 27200,936

View File

@ -0,0 +1,28 @@
scale 1000 1 0.5
rnode "well" 0 0 -265 267 0
rnode "Vdd" 0 -0 -237 285 0
resist "Vdd" "well" 8.53119
rnode "Gnd.t1" 0 0 29 -462 0
rnode "Gnd.t0" 0 0 112 -462 0
rnode "Gnd.n0" 0 0 62 -754 0
rnode "Gnd" 0 0 -22 -770 0
resist "Gnd" "Gnd.n0" 0.055709
resist "Gnd.n0" "Gnd.t1" 6.307
resist "Gnd.n0" "Gnd.t0" 1079.19
rnode "Vout.t1" 0 0 195 311 0
rnode "Vout.t0" 0 0 195 -462 0
rnode "Vout" 0 0 266 -211 0
resist "Vout" "Vout.t1" 3.28762
resist "Vout" "Vout.t0" 6.19274
rnode "Vdd" 0 0 -237 285 0
rnode "Vdd.t0" 0 0 29 379 0
rnode "Vdd.n0" 0 0 -153 379 0
rnode "well" 0 0 112 209 0
resist "Vdd.n0" "Vdd" 0.0465926
resist "Vdd.n0" "Vdd.t0" 3.18546
resist "well" "Vdd.n0" 8.48516
rnode "Vin.t0" 0 0 111 -158 0
rnode "Vin" 0 0 95 -213 0
resist "Vin" "Vin.t0" 7.52198
device msubckt sg13_lv_nmos 67 -562 68 -561 "Gnd.t0" "Vin.t0" 180 0 "Gnd.t1" 200 13600,536 "Vout.t0" 200 13600,536
device msubckt sg13_lv_pmos 67 9 68 10 "well" "Vin.t0" 180 0 "Vdd.t0" 400 27200,936 "Vout.t1" 400 27200,936

View File

@ -0,0 +1,25 @@
# Generated by kpex 0.3.7
crashbackups stop
drc off
gds read /home/pedersen/misc/inverter_example/layout/inverter_flat.gds
load inverter
select top cell
flatten inverter_flat
load inverter_flat
cellname delete inverter -noprompt
cellname rename inverter_flat inverter
select top cell
extract path /home/pedersen/misc/inverter_example/pex/pex_output/inverter_flat__inverter/magic_RC
extract all
ext2sim labels on
ext2sim -p /home/pedersen/misc/inverter_example/pex/pex_output/inverter_flat__inverter/magic_RC
extresist tolerance 1
extresist all
ext2spice short resistor
ext2spice merge conservative
ext2spice cthresh 0.02
ext2spice extresist on
ext2spice subcircuits top on
ext2spice format ngspice
ext2spice -p /home/pedersen/misc/inverter_example/pex/pex_output/inverter_flat__inverter/magic_RC -o /home/pedersen/misc/inverter_example/pex/pex_output/inverter_flat__inverter/magic_RC/inverter.pex.spice
quit -noprompt

View File

@ -0,0 +1,74 @@
import sys
import os
def extract_netlist_and_topcell(spice_path):
with open(spice_path, 'r') as f:
lines = f.readlines()
netlist = []
topcell = None
inside_subckt = False
for line in lines:
if line.strip().startswith(".subckt"):
parts = line.strip().split()
if len(parts) > 1:
topcell = parts[1]
inside_subckt = True
if inside_subckt:
netlist.append(line.rstrip())
if line.strip().startswith(".ends"):
break
if not topcell:
raise ValueError("Topcell name not found in SPICE file.")
return topcell, "\n".join(netlist)
def modify_sym_file(sym_path, output_dir, topcell, netlist):
with open(sym_path, 'r') as f:
sym_lines = f.readlines()
new_sym_lines = []
inserted = False
for line in sym_lines:
if line.strip().startswith("K {type=subcircuit"):
new_sym_lines.append(line)
continue
elif line.strip().startswith("template=") and not inserted:
new_template = (
f'template="name=x1\n'
f'schematic={topcell}\n'
f'spice_sym_def=\n'
f'\\\\"\n'
f'{netlist}\n'
f'\\\\"\n'
f'"'
)
new_sym_lines.append(new_template + "\n")
inserted = True
else:
new_sym_lines.append(line)
filename = os.path.basename(sym_path)
out_path = os.path.join(output_dir, filename)
os.makedirs(output_dir, exist_ok=True)
with open(out_path, 'w') as f:
f.writelines(new_sym_lines)
print(f"Saved modified .sym to: {out_path}")
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python insert_netlist_to_sym.py <netlist.spice> <symbol.sym> <output_folder>")
sys.exit(1)
spice_file = sys.argv[1]
sym_file = sys.argv[2]
output_folder = sys.argv[3]
topcell, netlist = extract_netlist_and_topcell(spice_file)
modify_sym_file(sym_file, output_folder, topcell, netlist)

View File

@ -0,0 +1,123 @@
import sys
import os
def load_text_file(file_path):
with open(file_path, 'r') as f:
return f.read()
def save_text_file(file_path, text):
with open(file_path, 'w') as f:
f.write(text)
def read_spice_definition(spice_path):
with open(spice_path, "r") as f:
lines = f.readlines()
subckt_line_index = next((i for i, line in enumerate(lines) if line.strip().lower().startswith(".subckt")), None)
if subckt_line_index is None:
raise ValueError(f"No .subckt line found in SPICE file: {spice_path}")
parts = lines[subckt_line_index].strip().split()
subckt_name = parts[1]
new_subckt_name = subckt_name
parts[1] = new_subckt_name
lines[subckt_line_index] = ' '.join(parts) + '\n'
definition_lines = []
recording = False
for line in lines:
if line.strip().lower().startswith(".subckt"):
recording = True
if recording:
definition_lines.append(line.rstrip())
if line.strip().lower() == ".ends":
break
return new_subckt_name, '\n'.join(definition_lines)
def find_subckt_location(subckt_name, schematic_file):
text_content = load_text_file(schematic_file)
return text_content.find(subckt_name + '.sym')
def find_first_curly_brackets_span(text, start_pos):
open_pos = text.find('{', start_pos)
if open_pos == -1:
return None, None
depth = 1
for i in range(open_pos + 1, len(text)):
if text[i] == '{':
depth += 1
elif text[i] == '}':
depth -= 1
if depth == 0:
return open_pos, i
return None, None
def replace_curly_brackets_content(text, start_idx, end_idx, new_content, subckt_name):
content = text[start_idx+1:end_idx].strip('\n ')
lines = content.splitlines()
name_line_index = next((i for i, line in enumerate(lines) if line.strip().startswith("name=")), None)
schematic_line = "schematic=" + subckt_name
if name_line_index is None:
new_lines = ['spice_sym_def="', new_content.strip(), '"'] + lines
else:
new_lines = (
lines[:name_line_index+1] +
[schematic_line] +
['spice_sym_def="', new_content.strip(), '"'] +
lines[name_line_index+1:]
)
return text[:start_idx+1] + '\n' + '\n'.join(new_lines) + '\n' + text[end_idx:]
def process_pair(spice_path, schematic_path):
print(f"\nProcessing:\n SPICE: {spice_path}\n Schematic: {schematic_path}")
try:
subckt_name, definition = read_spice_definition(spice_path)
print(f" Subcircuit name: {subckt_name}")
except Exception as e:
print(f" Error reading spice file: {e}")
return
# If subckt_name ends with '_pex', strip it for schematic search
search_subckt_name = subckt_name
if subckt_name.endswith("_pex"):
search_subckt_name = subckt_name[:-4]
location = find_subckt_location(search_subckt_name, schematic_path)
if location == -1:
print(f" '{search_subckt_name}.sym' not found in schematic file.")
return
schematic_content = load_text_file(schematic_path)
start_idx, end_idx = find_first_curly_brackets_span(schematic_content, location)
if start_idx is None:
print(" No matching curly brackets found after .sym device.")
return
new_schematic_content = replace_curly_brackets_content(
schematic_content, start_idx, end_idx, definition, subckt_name
)
# Save the file in the current working directory (where script is run)
base_name = os.path.basename(schematic_path)
name, ext = os.path.splitext(base_name)
new_filename = os.path.join(os.getcwd(), f"{name}_pex{ext}")
save_text_file(new_filename, new_schematic_content)
print(f" Replacement done and new schematic file saved as:\n {new_filename}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage:\n python3 script.py <schematic.sch> <spice_file.spice>")
sys.exit(1)
schematic = sys.argv[1]
spice_path = sys.argv[2]
print(f"Starting processing with schematic='{schematic}', spice='{spice_path}'") # Debug print
process_pair(spice_path, schematic)

View File

@ -0,0 +1,59 @@
import sys
import re
from pathlib import Path
def get_original_io_order(original_netlist_path):
io_pins = []
with open(original_netlist_path, "r") as f:
for line in f:
stripped = line.strip()
# Look for .subckt line, case-insensitive
if stripped.lower().startswith(".subckt"):
tokens = stripped.split()
if len(tokens) >= 3:
io_pins = tokens[2:]
break
if not io_pins:
raise ValueError("Could not find IO pins in original schematic")
return io_pins
def reorder_pex_subckt(pex_path, correct_order):
with open(pex_path, "r") as f:
lines = f.readlines()
new_lines = []
subckt_found = False
for line in lines:
if line.strip().lower().startswith(".subckt") and not subckt_found:
tokens = line.strip().split()
subckt_name = tokens[1]
ports = tokens[2:]
if set(ports) != set(correct_order):
raise ValueError("Port names in PEX netlist don't match original IO pins")
reordered_line = ".subckt " + subckt_name + "_pex " + " ".join(correct_order) + "\n"
new_lines.append(reordered_line)
subckt_found = True
else:
new_lines.append(line)
with open(pex_path, "w") as f:
f.writelines(new_lines)
print(f"Rewrote subckt line in {pex_path}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: python patch_pex_order.py <pex_spice_path> <original_schematic_spice_path>")
sys.exit(1)
pex_spice = Path(sys.argv[1])
original_schematic = Path(sys.argv[2])
io_order = get_original_io_order(original_schematic)
reorder_pex_subckt(pex_spice, io_order)

View File

@ -0,0 +1,37 @@
import sys
def generate_xschemrc(custom_paths):
lines = [
"# xschemrc - Custom configuration file for xschem",
"# This file sources another xschemrc file from a known location",
"",
"# Source the base configuration from a known location",
"source $::env(PDK_ROOT)/$::env(PDK)/libs.tech/xschem/xschemrc",
"",
"# (Optional) Add any custom overrides or extensions below",
'# set xschem_library_path /home/user/my_libs',
'# set xschem_gui_font "Monospace 10"',
"",
"append XSCHEM_LIBRARY_PATH :$PDK_ROOT/ihp-sg13g2/libs.tech/xschem"
]
# Append each custom path with the prefix "../"
for path in custom_paths:
lines.append(f"append XSCHEM_LIBRARY_PATH :{path}")
return "\n".join(lines) + "\n"
def main():
if len(sys.argv) < 2:
print("Usage: python3 make_xschemrc.py <path1> [<path2> ... <pathN>]")
sys.exit(1)
custom_paths = sys.argv[1:]
content = generate_xschemrc(custom_paths)
with open("xschemrc", "w") as f:
f.write(content)
print(f"xschemrc file generated with {len(custom_paths)} custom paths.")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,103 @@
#!/bin/bash
set -e
###############################################################################
# ⬇⬇⬇ USER CONFIGURATION ⬇⬇⬇ #
###############################################################################
# Activate Python environment (adjust this path to your Python venv activate script) Or if you python version is >3.12 ignore asuming klayout_pex is installed
PYTHON_ENV="$HOME/misc/klayout_pex/bin/activate"
# Example: /home/username/misc/klayout_pex/bin/activate
# Path to Magic executable used by kpex (adjust if installed elsewhere)
KPEX_MAGIC_EXE="$HOME/.local/bin/magic"
# Example: /usr/local/bin/magic or ~/.local/bin/magic
# Cell and schematic names (do NOT include file extensions)
SYM_DIR="../inverter.sym" # The name of your top cell / device under test (DUT)
#TESTBENCH_NAME="inverter_tb" # Name of your testbench schematic (without extension)
# Paths relative to this script or absolute paths
#SPICE_DIR="../simulations" # Directory containing netlist/spice files for testbench
LAYOUT_DIR="../layout/inverter_flat.gds" # Directory containing layout files (.gds etc.)
# Important: Path to your PDK root directory must be set externally in env variable PDK_ROOT
PDK_NAME="ihp_sg13g2" # Your PDK name (must match PDK_ROOT contents)
MAGICRC="$PDK_ROOT/$PDK/libs.tech/magic/ihp-sg13g2.magicrc"
# The magicrc file for your PDK, used during extraction
SCHEMATIC="../simulations/inverter.spice"
##############################################################################################################################################################
# ⛔ DO NOT TOUCH BELOW THIS LINE ⛔ Unless you see clear issue with your setup :) #
##############################################################################################################################################################
# Check if required files exist before proceeding
if [[ ! -f "$PYTHON_ENV" ]]; then
echo "[ERROR] Python environment activate script not found: $PYTHON_ENV"
exit 1
fi
if [[ ! -x "$KPEX_MAGIC_EXE" ]]; then
echo "[ERROR] Magic executable not found or not executable: $KPEX_MAGIC_EXE"
exit 1
fi
if [[ ! -f "$LAYOUT_DIR" ]]; then
echo "[ERROR] Layout GDS file not found: $LAYOUT_DIR"
exit 1
fi
if [[ ! -f "$MAGICRC" ]]; then
echo "[ERROR] Magicrc file for PDK not found: $MAGICRC"
exit 1
fi
# Activate Python virtual environment
echo "[INFO] Activating Python environment..."
source "$PYTHON_ENV"
echo "[INFO] Using MAGIC executable: $KPEX_MAGIC_EXE"
# Run parasitic extraction with kpex
echo "[INFO] Running parasitic extraction with KPEX..."
kpex \
--pdk "$PDK_NAME" \
--magic \
--schematic "$SCHEMATIC"\
--gds "$LAYOUT_DIR" \
--magicrc "$MAGICRC" \
--magic_mode RC \
--magic_cthresh 0.02 \
--magic_rthresh 50 \
--magic_short resistor \
--magic_merge conservative \
--out_dir ./pex_output
# Find the generated spice file (assuming only one)
spice_location=$(find ./pex_output -type f -name "*.spice" ! -name "*_dummy_schematic.spice" | head -n 1)
if [[ -z "$spice_location" ]]; then
echo "[ERROR] No .spice file found in pex_output directory"
exit 1
fi
echo "[INFO] Found extracted spice file: $spice_location"
# Run Python script to fix port ordering in the extracted netlist
echo "[INFO] Reordering subcircuit pins to match original schematic..."
echo "$spice_location"
echo "$SCHEMATIC"
#python3 python/match_subckt_order.py "$spice_location" "$SCHEMATIC"
echo "[INFO] Creating DUT symbol with pex netlist"
python3 python/insert_netlist_to_sym.py "$spice_location" "$SYM_DIR" xschem_pex_symbol
cd xschem_pex_symbol
echo "[✅ DONE] Modified symbol, including pex netlist generated and saved in: $(pwd)/"

View File

@ -0,0 +1,84 @@
v {xschem version=3.4.6 file_version=1.2}
G {}
K {}
V {}
S {}
E {}
B 2 710 -550 1510 -150 {flags=graph
y1=-0.0023
y2=1.3
ypos1=0
ypos2=2
divy=5
subdivy=1
unity=1
x1=0
x2=2e-06
divx=5
subdivx=1
xlabmag=1.0
ylabmag=1.0
node=vout
color=4
dataset=-1
unitx=1
logx=0
logy=0
}
N 150 -170 150 -140 {lab=Vin}
N 70 -170 70 -140 {lab=Vdd}
N 70 -80 70 -60 {lab=GND}
N 110 -60 150 -60 {lab=GND}
N 150 -80 150 -60 {lab=GND}
N 110 -60 110 -50 {lab=GND}
N 70 -60 110 -60 {lab=GND}
N 320 -410 320 -380 {lab=Vdd}
N 320 -210 320 -190 {lab=GND}
N 220 -300 240 -300 {lab=Vin}
N 520 -300 540 -300 {lab=Vout}
C {vsource.sym} 150 -110 0 0 {name=V1 value="PULSE(0 1.2 0.5u 10n 10n 1u 2u 1)" savecurrent=false}
C {vsource.sym} 70 -110 0 0 {name=V2 value=1.2 savecurrent=false}
C {gnd.sym} 110 -50 0 0 {name=l2 lab=GND}
C {lab_pin.sym} 150 -170 0 0 {name=p1 sig_type=std_logic lab=Vin}
C {lab_pin.sym} 70 -170 0 0 {name=p3 sig_type=std_logic lab=Vdd}
C {code_shown.sym} 40 -660 0 0 {name=NGSPICE
only_toplevel=true
value="
.control
save all
tran 50n 2u
write test_inverter.raw
.endc
" }
C {devices/code_shown.sym} 280 -540 0 0 {name=MODEL only_toplevel=true
format="tcleval( @value )"
value="
.lib cornerMOSlv.lib mos_tt
.lib cornerRES.lib res_typ
"}
C {launcher.sym} 770 -120 0 0 {name=h5
descr="load waves"
tclcommand="xschem raw_read $netlist_dir/test_inverter.raw tran"
}
C {inverter.sym} 390 -300 0 0 {
name=x1
schematic=inverter_pex
spice_sym_def="
.subckt inverter_pex Vdd Vin Vout Gnd
X0 Vout Vin Vdd Vdd sg13_lv_pmos ad=0.68p pd=4.68u as=0.68p ps=4.68u w=2u l=0.45u
X1 Vout Vin Gnd Gnd sg13_lv_nmos ad=0.34p pd=2.68u as=0.34p ps=2.68u w=1u l=0.45u
C0 Vout Vin 0.10077f
C1 Vin Vdd 0.14482f
C2 Vout Vdd 0.13155f
R0 Vin Vin.n0 7.52248
C3 Vout Gnd 0.39245f
C4 Vin Gnd 0.64666f
C5 Vdd Gnd 0.15308f
.ends
"
}
C {lab_pin.sym} 220 -300 0 0 {name=p2 sig_type=std_logic lab=Vin}
C {gnd.sym} 320 -190 0 0 {name=l1 lab=GND}
C {lab_pin.sym} 320 -410 0 0 {name=p4 sig_type=std_logic lab=Vdd}
C {lab_pin.sym} 540 -300 0 1 {name=p5 sig_type=std_logic lab=Vout}

View File

@ -0,0 +1,12 @@
# xschemrc - Custom configuration file for xschem
# This file sources another xschemrc file from a known location
# Source the base configuration from a known location
source $::env(PDK_ROOT)/$::env(PDK)/libs.tech/xschem/xschemrc
# (Optional) Add any custom overrides or extensions below
# set xschem_library_path /home/user/my_libs
# set xschem_gui_font "Monospace 10"
append XSCHEM_LIBRARY_PATH :$PDK_ROOT/ihp-sg13g2/libs.tech/xschem
append XSCHEM_LIBRARY_PATH :../../

View File

@ -0,0 +1,45 @@
v {xschem version=3.4.6 file_version=1.2}
G {}
K {type=subcircuit
format="@name @pinlist @symname"
template="name=x1
schematic=inverter
spice_sym_def=
\\"
.subckt inverter well Vout Vin Gnd Vdd
R0 Vdd well 0.000000
X0 Vout Vin Vdd Vdd sg13_lv_pmos ad=0.68p pd=4.68u as=0.68p ps=4.68u w=2u l=0.45u
X1 Vout Vin Gnd Gnd sg13_lv_nmos ad=0.34p pd=2.68u as=0.34p ps=2.68u w=1u l=0.45u
C0 Vdd Vout 0.13155f
C1 Vin Vout 0.10077f
C2 Vin Vdd 0.14482f
R1 Vdd well 8.53169
R2 Vin Vin.n0 7.52248
R3 well Vdd 8.53069
C3 Vout Gnd 0.39245f
C4 Vin Gnd 0.64666f
C5 Vdd Gnd 0.15308f
.ends
\\"
"
V {}
S {}
E {}
L 7 -70 -80 -70 -60 {}
L 7 -150 0 -130 0 {}
L 7 110 0 130 0 {}
L 7 -70 70 -70 90 {}
B 5 -72.5 -82.5 -67.5 -77.5 {name=Vdd dir=inout}
B 5 -152.5 -2.5 -147.5 2.5 {name=Vin dir=inout}
B 5 127.5 -2.5 132.5 2.5 {name=Vout dir=inout}
B 5 -72.5 87.5 -67.5 92.5 {name=Gnd dir=inout}
A 4 105 0 7.071067811865476 135 360 {}
P 4 5 100 0 -130 -80 -130 90 100 0 100 0 {}
T {@symname
} -84 -6 0 0 0.3 0.3 {}
T {@name} -45 -32 0 0 0.2 0.2 {}
T {Vdd} -74 -55 3 1 0.2 0.2 {}
T {Vin} -125 -4 0 0 0.2 0.2 {}
T {Vout} 80 -9 0 1 0.2 0.2 {}
T {Gnd} -66 65 1 1 0.2 0.2 {}

View File

@ -0,0 +1,42 @@
v {xschem version=3.4.6 file_version=1.2}
G {}
K {type=subcircuit
format="@name @pinlist @symname"
template="name=x1
schematic=inverter_pex
spice_sym_def=
\\"
.subckt inverter_pex Vdd Vin Vout Gnd
X0 Vout Vin Vdd Vdd sg13_lv_pmos ad=0.68p pd=4.68u as=0.68p ps=4.68u w=2u l=0.45u
X1 Vout Vin Gnd Gnd sg13_lv_nmos ad=0.34p pd=2.68u as=0.34p ps=2.68u w=1u l=0.45u
C0 Vout Vin 0.10077f
C1 Vin Vdd 0.14482f
C2 Vout Vdd 0.13155f
R0 Vin Vin.n0 7.52248
C3 Vout Gnd 0.39245f
C4 Vin Gnd 0.64666f
C5 Vdd Gnd 0.15308f
.ends
\\"
"
V {}
S {}
E {}
L 7 -70 -80 -70 -60 {}
L 7 -150 0 -130 0 {}
L 7 110 0 130 0 {}
L 7 -70 70 -70 90 {}
B 5 -72.5 -82.5 -67.5 -77.5 {name=Vdd dir=inout}
B 5 -152.5 -2.5 -147.5 2.5 {name=Vin dir=inout}
B 5 127.5 -2.5 132.5 2.5 {name=Vout dir=inout}
B 5 -72.5 87.5 -67.5 92.5 {name=Gnd dir=inout}
A 4 105 0 7.071067811865476 135 360 {}
P 4 5 100 0 -130 -80 -130 90 100 0 100 0 {}
T {@symname
} -84 -6 0 0 0.3 0.3 {}
T {@name} -45 -32 0 0 0.2 0.2 {}
T {Vdd} -74 -55 3 1 0.2 0.2 {}
T {Vin} -125 -4 0 0 0.2 0.2 {}
T {Vout} 80 -9 0 1 0.2 0.2 {}
T {Gnd} -66 65 1 1 0.2 0.2 {}

View File

@ -0,0 +1,74 @@
import sys
import os
def extract_netlist_and_topcell(spice_path):
with open(spice_path, 'r') as f:
lines = f.readlines()
netlist = []
topcell = None
inside_subckt = False
for line in lines:
if line.strip().startswith(".subckt"):
parts = line.strip().split()
if len(parts) > 1:
topcell = parts[1]
inside_subckt = True
if inside_subckt:
netlist.append(line.rstrip())
if line.strip().startswith(".ends"):
break
if not topcell:
raise ValueError("Topcell name not found in SPICE file.")
return topcell, "\n".join(netlist)
def modify_sym_file(sym_path, output_dir, topcell, netlist):
with open(sym_path, 'r') as f:
sym_lines = f.readlines()
new_sym_lines = []
inserted = False
for line in sym_lines:
if line.strip().startswith("K {type=subcircuit"):
new_sym_lines.append(line)
continue
elif line.strip().startswith("template=") and not inserted:
new_template = (
f'template="name=x1\n'
f'schematic={topcell}\n'
f'spice_sym_def=\n'
f'\\\\"\n'
f'{netlist}\n'
f'\\\\"\n'
f'"'
)
new_sym_lines.append(new_template + "\n")
inserted = True
else:
new_sym_lines.append(line)
filename = os.path.basename(sym_path)
out_path = os.path.join(output_dir, filename)
os.makedirs(output_dir, exist_ok=True)
with open(out_path, 'w') as f:
f.writelines(new_sym_lines)
print(f"Saved modified .sym to: {out_path}")
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python insert_netlist_to_sym.py <netlist.spice> <symbol.sym> <output_folder>")
sys.exit(1)
spice_file = sys.argv[1]
sym_file = sys.argv[2]
output_folder = sys.argv[3]
topcell, netlist = extract_netlist_and_topcell(spice_file)
modify_sym_file(sym_file, output_folder, topcell, netlist)

View File

@ -0,0 +1,18 @@
# xschemrc - Custom configuration file for xschem
# This file sources another xschemrc file from a known location
# Source the base configuration from a known location
source $::env(PDK_ROOT)/$::env(PDK)/libs.tech/xschem/xschemrc
# (Optional) Add any custom overrides or extensions below
# set xschem_library_path /home/user/my_libs
# set xschem_gui_font "Monospace 10"
#### include skywater libraries. Here I use [pwd]. This works if I start xschem from here.
###only if you dont have this setup already ###
###append XSCHEM_LIBRARY_PATH :[file dirname [info script]]
#### Add custom libraries (directories with .lib files)
append XSCHEM_LIBRARY_PATH :$PDK_ROOT/ihp-sg13g2/libs.tech/xschem

View File

@ -27,6 +27,29 @@ node "a_1245_3300#" 64 1428.73 1245 3300 pdif 0 0 0 0 646952 33468 462832 13040
node "w_805_2869#" 1242572 5.18828 805 2869 pw 182658 17438 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
node "vdd" 58 17973.9 127 335 m6 0 0 0 0 2395440 52908 883744 19072 642880 13104 642880 13104 1340864 17360 6062752 36968 0 0
substrate "gnd" 0 0 -513 -440 m7 0 0 0 0 2036008 30746 224000 4320 224000 4320 224000 4320 654400 6472 1331200 8256 9608000 48040
cap "out-" "a_687_2445#" 894.529
cap "w_805_2869#" "V+" 10.369
cap "vdd" "V-" 4062.84
cap "out-" "a_1245_3300#" 367.947
cap "vbias" "clk" 198.783
cap "out-" "a_944_1911#" 611.611
cap "w_805_2869#" "a_687_2445#" 3.06666
cap "clk" "a_687_2445#" 238.332
cap "w_805_2869#" "a_1245_3300#" 12.1813
cap "clk" "a_1245_3300#" 447.822
cap "w_805_2869#" "a_944_1911#" 4.42903
cap "clk" "a_944_1911#" 247.428
cap "out-" "V-" 3.46102
cap "vdd" "a_1752_817#" 7111.46
cap "w_805_2869#" "V-" 10.604
cap "vdd" "out+" 1847.73
cap "V+" "a_687_2445#" 130.641
cap "V+" "a_1245_3300#" 769.244
cap "vbias" "a_687_2445#" 4.05638
cap "V+" "a_944_1911#" 1081.99
cap "vbias" "a_1245_3300#" 2.78546
cap "a_687_2445#" "a_1245_3300#" 11353.4
cap "vbias" "a_944_1911#" 3.01856
cap "a_944_1911#" "a_687_2445#" 2249.36
cap "clk" "a_1752_817#" 502.499
cap "out-" "out+" 93.2996
@ -59,29 +82,6 @@ cap "vdd" "a_944_1911#" 1108.93
cap "out-" "V+" 7.34235
cap "V-" "out+" 9.67399
cap "out-" "vbias" 0.0904886
cap "out-" "a_687_2445#" 894.529
cap "w_805_2869#" "V+" 10.369
cap "vdd" "V-" 4062.84
cap "out-" "a_1245_3300#" 367.947
cap "vbias" "clk" 198.783
cap "out-" "a_944_1911#" 611.611
cap "w_805_2869#" "a_687_2445#" 3.06666
cap "clk" "a_687_2445#" 238.332
cap "w_805_2869#" "a_1245_3300#" 12.1813
cap "clk" "a_1245_3300#" 447.822
cap "w_805_2869#" "a_944_1911#" 4.42903
cap "clk" "a_944_1911#" 247.428
cap "out-" "V-" 3.46102
cap "vdd" "a_1752_817#" 7111.46
cap "w_805_2869#" "V-" 10.604
cap "vdd" "out+" 1847.73
cap "V+" "a_687_2445#" 130.641
cap "V+" "a_1245_3300#" 769.244
cap "vbias" "a_687_2445#" 4.05638
cap "V+" "a_944_1911#" 1081.99
cap "vbias" "a_1245_3300#" 2.78546
cap "a_687_2445#" "a_1245_3300#" 11353.4
cap "vbias" "a_944_1911#" 3.01856
device msubckt sg13_lv_nmos 3710 1075 3711 1076 l=40 w=800 "gnd" "gnd" 80 0 "gnd" 0 0 "gnd" 1600 30400,876
device msubckt sg13_lv_nmos 3594 1075 3595 1076 l=40 w=800 "gnd" "clk" 80 0 "a_944_1911#" 800 57600,944 "gnd" 800 30400,876
device msubckt sg13_lv_nmos 3410 1075 3411 1076 l=40 w=800 "gnd" "a_687_2445#" 80 0 "gnd" 800 30400,876 "a_944_1911#" 800 57600,944

View File

@ -6,10 +6,6 @@ set -e
###############################################################################
# Path to Magic executable used by kpex (adjust if installed elsewhere)
KPEX_MAGIC_EXE="$HOME/.local/bin/magic"
# Example: /usr/local/bin/magic or ~/.local/bin/magic
#Path to symbol
SYM_DIR="./DIFF_COMPARATOR.sym" # Asuming you have a symbol for the DUT
@ -27,11 +23,6 @@ SCHEMATIC="../layout/lvs_netlist/DIFF_COMPARATOR.spice" # Spicefile of the compa
if [[ ! -x "$KPEX_MAGIC_EXE" ]]; then
echo "[ERROR] Magic executable not found or not executable: $KPEX_MAGIC_EXE"
exit 1
fi
if [[ ! -f "$LAYOUT_DIR" ]]; then
echo "[ERROR] Layout GDS file not found: $LAYOUT_DIR"
exit 1