blob: 556ab741ef0c15b80be03f50a7e6339e8c305b8a [file] [log] [blame]
#!/usr/bin/env python3
#
#--------------------------------------------------------------------
# Add a \"device\" property to the layout of specific PDK primitive
# devices that do not extract as intended. This includes, for
# example, RF devices (which extract as non-RF devices), vertical
# parallel plate capacitors (which extract as metal wires), and any
# other device that is not represented in magic's \"extract\" section.
#
# This uses the method introduced in magic version 8.3.298 using
# property "device" with value "primitive", so that the subcell is
# output to the netlist without a corresponding subcircuit entry or
# black box entry, as the device is assumed to be defined in the PDK.
# But it is necessary to make sure that the ports are in the correct
# order for the PDK device.
#--------------------------------------------------------------------
import os
import re
import sys
import subprocess
options = []
arguments = []
for item in sys.argv[1:]:
if item.find('-', 0) == 0:
options.append(item[1:])
else:
arguments.append(item)
ef_format = False
variant = 'sky130A'
magpath = variant + '/libs.ref/sky130_fd_pr/mag'
maglefpath = variant + '/libs.ref/sky130_fd_pr/maglef'
spicepath = variant + '/libs.ref/sky130_fd_pr/spice'
if len(options) > 0:
for option in options:
if option.startswith('variant'):
variant = option.split('=')[1]
magpath = variant + '/libs.ref/sky130_fd_pr/mag'
maglefpath = variant + '/libs.ref/sky130_fd_pr/maglef'
spicepath = variant + '/libs.ref/sky130_fd_pr/spice'
for option in options:
if option == 'ef_format':
ef_format = True
magpath = variant + '/libs.ref/mag/sky130_fd_pr'
maglefpath = variant + '/libs.ref/maglef/sky130_fd_pr'
spicepath = variant + '/libs.ref/spice/sky130_fd_pr'
elif len(arguments) > 0:
magpath = arguments[0]
maglefpath = magpath.replace('/mag/', '/maglef/')
spicepath = magpath.replace('/mag/', '/spice/')
# \"devlist\" is an enumeration of all devices that have both a layout
# in libs.ref/sky130_fd_pr/mag and corresponding subcircuit models
# in libs.ref/sky130_fd_pr/spice, and for which extracting the layout
# in magic does not produce the intended SPICE model.
devlist = ['sky130_fd_pr__cap_vpp_02p4x04p6_m1m2_noshield',
'sky130_fd_pr__cap_vpp_02p7x06p1_m1m2m3m4_shieldl1_fingercap',
'sky130_fd_pr__cap_vpp_02p7x11p1_m1m2m3m4_shieldl1_fingercap',
'sky130_fd_pr__cap_vpp_02p7x21p1_m1m2m3m4_shieldl1_fingercap',
'sky130_fd_pr__cap_vpp_02p7x41p1_m1m2m3m4_shieldl1_fingercap',
'sky130_fd_pr__cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_fingercap2',
'sky130_fd_pr__cap_vpp_03p9x03p9_m1m2_shieldl1_floatm3',
'sky130_fd_pr__cap_vpp_04p4x04p6_l1m1m2_noshield',
'sky130_fd_pr__cap_vpp_04p4x04p6_l1m1m2_shieldpo_floatm3',
'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield',
'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o2',
'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o2',
'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_shieldl1',
'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1',
'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4',
'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4',
'sky130_fd_pr__cap_vpp_05p9x05p9_m1m2m3m4_shieldl1_wafflecap',
'sky130_fd_pr__cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4',
'sky130_fd_pr__cap_vpp_06p8x06p1_m1m2m3_shieldl1m4',
'sky130_fd_pr__cap_vpp_08p6x07p8_l1m1m2_noshield',
'sky130_fd_pr__cap_vpp_08p6x07p8_l1m1m2_shieldpo_floatm3',
'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_noshield',
'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_shieldl1',
'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1',
'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4',
'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4',
'sky130_fd_pr__cap_vpp_11p3x11p3_m1m2m3m4_shieldl1_wafflecap',
'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhv',
'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2_noshield',
'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2_shieldpom3',
'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3_shieldm4',
'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3_shieldpom4',
'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5',
'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5',
'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5_x',
'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5_x',
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_noshield',
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_shieldl1',
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1',
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4',
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4',
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5',
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldm5',
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m4_noshield',
'sky130_fd_pr__cap_vpp_44p7x23p1_pol1m1m2m3m4m5_noshield',
'sky130_fd_pr__rf_nfet_01v8_bM02W1p65L0p15',
'sky130_fd_pr__rf_nfet_01v8_bM02W1p65L0p18',
'sky130_fd_pr__rf_nfet_01v8_bM02W1p65L0p25',
'sky130_fd_pr__rf_nfet_01v8_bM02W3p00L0p15',
'sky130_fd_pr__rf_nfet_01v8_bM02W3p00L0p18',
'sky130_fd_pr__rf_nfet_01v8_bM02W3p00L0p25',
'sky130_fd_pr__rf_nfet_01v8_bM02W5p00L0p15',
'sky130_fd_pr__rf_nfet_01v8_bM02W5p00L0p18',
'sky130_fd_pr__rf_nfet_01v8_bM02W5p00L0p25',
'sky130_fd_pr__rf_nfet_01v8_bM04W1p65L0p15',
'sky130_fd_pr__rf_nfet_01v8_bM04W1p65L0p18',
'sky130_fd_pr__rf_nfet_01v8_bM04W1p65L0p25',
'sky130_fd_pr__rf_nfet_01v8_bM04W3p00L0p15',
'sky130_fd_pr__rf_nfet_01v8_bM04W3p00L0p18',
'sky130_fd_pr__rf_nfet_01v8_bM04W3p00L0p25',
'sky130_fd_pr__rf_nfet_01v8_bM04W5p00L0p15',
'sky130_fd_pr__rf_nfet_01v8_bM04W5p00L0p18',
'sky130_fd_pr__rf_nfet_01v8_bM04W5p00L0p25',
'sky130_fd_pr__rf_nfet_01v8_lvt_aF02W0p42L0p15',
'sky130_fd_pr__rf_nfet_01v8_lvt_aF02W0p84L0p15',
'sky130_fd_pr__rf_nfet_01v8_lvt_aF02W3p00L0p15',
'sky130_fd_pr__rf_nfet_01v8_lvt_aF04W0p84L0p15',
'sky130_fd_pr__rf_nfet_01v8_lvt_aF04W3p00L0p15',
'sky130_fd_pr__rf_nfet_01v8_lvt_aF08W0p84L0p15',
'sky130_fd_pr__rf_nfet_01v8_lvt_aF08W3p00L0p15',
'sky130_fd_pr__rf_nfet_01v8_lvt_bM02W5p00L0p18',
'sky130_fd_pr__rf_nfet_01v8_lvt_bM02W5p00L0p25',
'sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p18',
'sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p25',
'sky130_fd_pr__rf_nfet_g5v0d10v5_bM02W3p00L0p50',
'sky130_fd_pr__rf_nfet_g5v0d10v5_bM02W5p00L0p50',
'sky130_fd_pr__rf_nfet_g5v0d10v5_bM04W3p00L0p50',
'sky130_fd_pr__rf_nfet_g5v0d10v5_bM04W5p00L0p50',
'sky130_fd_pr__rf_nfet_g5v0d10v5_bM04W7p00L0p50',
'sky130_fd_pr__rf_nfet_g5v0d10v5_bM10W3p00L0p50',
'sky130_fd_pr__rf_nfet_g5v0d10v5_bM10W5p00L0p50',
'sky130_fd_pr__rf_nfet_g5v0d10v5_bM10W7p00L0p50',
'sky130_fd_pr__rf_pfet_01v8_aF02W0p84L0p15',
'sky130_fd_pr__rf_pfet_01v8_aF02W1p68L0p15',
'sky130_fd_pr__rf_pfet_01v8_aF02W3p00L0p15',
'sky130_fd_pr__rf_pfet_01v8_aF02W5p00L0p15',
'sky130_fd_pr__rf_pfet_01v8_aF04W1p68L0p15',
'sky130_fd_pr__rf_pfet_01v8_bM02W1p65L0p15',
'sky130_fd_pr__rf_pfet_01v8_bM02W1p65L0p18',
'sky130_fd_pr__rf_pfet_01v8_bM02W1p65L0p25',
'sky130_fd_pr__rf_pfet_01v8_bM02W3p00L0p15',
'sky130_fd_pr__rf_pfet_01v8_bM02W3p00L0p18',
'sky130_fd_pr__rf_pfet_01v8_bM02W3p00L0p25',
'sky130_fd_pr__rf_pfet_01v8_bM02W5p00L0p15',
'sky130_fd_pr__rf_pfet_01v8_bM02W5p00L0p18',
'sky130_fd_pr__rf_pfet_01v8_bM02W5p00L0p25',
'sky130_fd_pr__rf_pfet_01v8_bM04W1p65L0p15',
'sky130_fd_pr__rf_pfet_01v8_bM04W1p65L0p18',
'sky130_fd_pr__rf_pfet_01v8_bM04W1p65L0p25',
'sky130_fd_pr__rf_pfet_01v8_bM04W3p00L0p15',
'sky130_fd_pr__rf_pfet_01v8_bM04W3p00L0p18',
'sky130_fd_pr__rf_pfet_01v8_bM04W3p00L0p25',
'sky130_fd_pr__rf_pfet_01v8_bM04W5p00L0p15',
'sky130_fd_pr__rf_pfet_01v8_bM04W5p00L0p18',
'sky130_fd_pr__rf_pfet_01v8_bM04W5p00L0p25',
'sky130_fd_pr__rf_pfet_01v8_mvt_aF02W0p84L0p15']
# The portlist array contains the names of the ports used in the layout in the order that
# they appear in the device subcircuit model. Note that none of the pin names match between
# the layout and device definition, so the usual annotation method in magic won't work.
portlist = [ ['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET3'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET3'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET4'],
['C0', 'C1', 'SUB', 'MET4'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET3'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET3'],
['C0', 'C1', 'SUB', 'MET4'],
['C0', 'C1', 'SUB', 'MET4'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB', 'MET5'],
['C0', 'C1', 'SUB'],
['C0', 'C1', 'SUB'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK'],
['DRAIN', 'GATE', 'SOURCE', 'BULK']]
#--------------------------------------------------------------------
# Do this for files both in mag/ and maglef/ . . .
pathlist = [magpath, maglefpath]
for idx, device in enumerate(devlist):
for path in pathlist:
infile_name = path + '/' + device + '.mag'
if not os.path.exists(infile_name):
print('Error: Cannot find file ' + infile_name)
continue
print('Adding special extraction property to device ' + device + ' layout')
propcmd = ['../common/insert_property.py', variant, 'sky130_fd_pr',
device, 'device primitive']
if ef_format:
propcmd.append('-ef_format')
# Just call the insert_property.py script
subprocess.run(propcmd, stdout = subprocess.DEVNULL, stderr = subprocess.DEVNULL)
# Now renumber the ports in the layout to match the PDK device. The port names in
# the layout generally don't match the port names in the PDK device definitions,
# so ports are handled by manually listing them all out.
devports = portlist[idx]
modified = False
with open(infile_name, 'r') as ifile:
magdata = ifile.read()
outlines = []
for line in magdata.splitlines():
if line.startswith('port'):
if lname and lname in devports:
tokens = line.split()
newindex = str(1 + devports.index(lname))
if newindex != tokens[1]:
modified = True
tokens[1] = newindex
newline = ' '.join(tokens)
else:
newline = line
lname = None
elif line.startswith('flabel'):
lname = line.split()[-1]
newline = line
elif line.startswith('rlabel'):
lname = line.split()[-1]
newline = line
else:
lname = None
newline = line
outlines.append(newline)
if modified:
print('Modifying port order in ' + infile_name)
with open(infile_name, 'w') as ofile:
ofile.write('\n'.join(outlines))
# Any SPICE netlist for this device that came from extraction in magic during the
# PDK build is invalid, and should be removed.
infile_name = spicepath + '/' + device + '.spice'
if os.path.isfile(infile_name):
os.remove(infile_name)