blob: c02d5861833a993d65d1ec0e9b34142df10ad15d [file] [log] [blame]
#!/usr/bin/env python3
import copy
import csv
import math
import pprint
import re
import subprocess
import sys
import traceback
from collections import defaultdict
import common
EXAMPLE_CSV = dict(r for r in csv.reader(open('examples.csv', 'r', newline='')))
def pformat(*args, **kw):
return pprint.pformat(*args, width=200, **kw)
def assert_pformat(a, b, d):
return repr((a, b))+'\n'+pformat(d)
TESTED_STRINGS = set()
def p(s):
assert s not in TESTED_STRINGS, s
x = parse_name(s)
pprint.pprint(x, width=40)
TESTED_STRINGS.add(s)
print(newname(x[0]))
def insert(d, pv):
"""
>>> d = []
>>> insert(d, ('a', 'b'))
>>> d
[('a', 'b')]
>>> insert(d, ('a', 'b'))
>>> d
[('a', 'b')]
>>> insert(d, ('c', 'd'))
>>> d
[('a', 'b'), ('c', 'd')]
>>> insert(d, ('a', 'c')) # doctest: +ELLIPSIS
Traceback (most recent call last):
...
AssertionError: ...
"""
if pv[0] == 'prop':
d.append(pv)
return
existing = []
for k, v in d:
if k == pv[0]:
existing.append((k, v))
if existing:
assert pv in existing, assert_pformat(pv, existing, d)
if pv not in d:
d.append(pv)
RE_DUPLICATE = re.compile("^(?P<x>(?P<one>.*?)(rf2?)?)_*(?P=one)$")
def fix_duplicate(s):
"""
>>> fix_duplicate("xcmvpp2_raphaelxcmvpp2_raphael")
'xcmvpp2_raphael'
xcmvpp4p4x4p6_polym5shieldrf2_
xcmvpp4p4x4p6_polym5shield
>>> fix_duplicate("xcmvpp4p4x4p6_polym5shieldrf2_xcmvpp4p4x4p6_polym5shield")
'xcmvpp4p4x4p6_polym5shieldrf2'
xcmvpp11p5x11p7_polym5modshield_raphael
xcmvpp11p5x11p7_polym5modshield_raphael
>>> fix_duplicate("xcmvpp11p5x11p7_polym5modshield_raphaelxcmvpp11p5x11p7_polym5modshield_raphael")
'xcmvpp11p5x11p7_polym5modshield_raphael'
"""
m = RE_DUPLICATE.match(s)
if not m:
return s
return m.group('x')
LAYERS = {
'deep nwell', # Deep-Nwell
'psub' , # P Substrate
'pwell' , # Pwell
'nwell' , # Nwell
'ndiff' , # N Diffusion
'pdiff' , # P Diffusion
'poly' , # Poly
'li' , # Local-interconnect
'm1' , # Metal 1
'm2' , # Metal 2
'm3' , # Metal 3
'm4' , # Metal 4
'm5' , # Metal 5
'rdl' , # Redistribution Layer 1
'x' , # FIXME: Remove
}
LAYERS_MAPPING = [
('X1', 'deep nwell'),
('X2', 'psub' ),
('pw', 'pwell'),
('nw', 'nwell'),
('dn', 'ndiff'), # N Diffusion
('dp', 'pdiff'), # N Diffusion
('fltpoly', 'poly' ), # FIXME: What is this?
('poly', 'poly' ),
('po', 'poly' ),
('p1', 'poly' ),
('py', 'poly' ),
('rp', 'poly' ), # FIXME: What is this?
('li', 'li' ),
('l1', 'li' ),
('m1', 'm1' ),
('m2', 'm2' ),
('m3', 'm3' ),
('m4', 'm4' ),
('m5', 'm5' ),
('rdl', 'rdl' ), # Redistribution layer.
('x', 'x' ), # FIXME:
]
for _, t in LAYERS_MAPPING:
assert t in LAYERS, t
def get_layers(s):
"""
>>> get_layers('m5')
(['m5'], '')
>>> get_layers('m5li')
(['li', 'm5'], '')
>>> get_layers('polym5li')
(['poly', 'li', 'm5'], '')
>>> get_layers('p1l1')
(['poly', 'li'], '')
>>> get_layers('lishield')
(['li'], 'shield')
>>> get_layers('l1m1con')
(['li', 'm1'], 'con')
>>> get_layers('rpl1con')
(['poly', 'li'], 'con')
>>> get_layers('m5rdl_173911084')
(['m5', 'rdl'], '_173911084')
"""
o = []
for f, t in LAYERS_MAPPING:
if f in s:
s = s.replace(f, '')
o.append(t)
o.sort(key=layer_sort_key)
return o, s
def layer_sort_key(l):
"""
>>> d = ['m5', 'm1', 'po']
>>> d.sort(key=layer_sort_key)
>>> d
['po', 'm1', 'm5']
>>> d = ['rdl', 'deep nwell', 'psub']
>>> d.sort(key=layer_sort_key)
>>> d
['deep nwell', 'psub', 'rdl']
"""
layer_index1 = [x[0] for x in LAYERS_MAPPING]
layer_index2 = [x[1] for x in LAYERS_MAPPING]
layer_index3 = [NEWNAME_LAYERS[x[1]] for x in LAYERS_MAPPING]
if l in layer_index1:
i = layer_index1.index(l)
elif l in layer_index2:
i = layer_index2.index(l)
elif l in layer_index3:
i = layer_index3.index(l)
else:
assert False, l
i = float('inf')
return (i, l)
def csv_parse_layers(s, x):
x = eval(x)
for l in x:
assert l in LAYERS, (l, x, s)
return x
def parse_common(s):
"""
>>> parse_common('')
('', {'esd': False, 'rf': False})
>>> parse_common('rf')
('', {'esd': False, 'rf': True})
>>> parse_common('_ttleak')
('', {'corners': ['tt', 'leak'], 'esd': False, 'rf': False})
>>> parse_common('sonos_ffteol')
('sonos', {'corners': ['ff', 'teol'], 'esd': False, 'rf': False})
>>> parse_common('sonos_ffeol_p')
('sonos_p', {'corners': ['ff', 'eol'], 'esd': False, 'rf': False})
>>> parse_common('s8rf_pshort_W1p65_L0p25_M2')
('pshort_w1p65_l0p25_m2', {'esd': False, 'rf': True})
>>> parse_common('s8rf_pnp5x')
('pnp5x', {'esd': False, 'rf': True})
"""
params = {}
s = s.lower()
s = RE_S8RF.sub('\\1_', s)
# Extract any corner information
corners = [
('base', 'base' ),
('ff', 'ff' ),
('fs', 'fs' ),
('sf', 'sf' ),
('ss', 'ss' ),
('tt', 'tt' ),
('leak', 'leak' ),
('correln', 'correln' ),
('correlp', 'correlp' ),
('wafer', 'wafer' ),
('subvt', 'subvt' ),
('eol', 'eol' ), # End of life
('bol', 'bol' ), # Begin of life
('tbol', 'tbol' ), # Typical? Begin of life
('wbol', 'wbol' ), # Worst? Begin of life
('teol', 'teol' ), # Typical? End of life
('weol', 'weol' ), # Worst? End of life
('mm', 'mismatch' ), # Mismatch
('discrete', 'discrete' ),
('subcircuit', 'subcircuit'),
# FIXME: What are these!?
('debug', 'debug' ),
('fixed', 'fixed' ),
('symbolic', 'symbolic' ),
]
found_corners = []
# FIXME!!!
for (i, b) in corners:
if '_'+i in s:
s = s.replace('_'+i, '_')
found_corners.append(b)
short_corners = [
'f',
't',
's',
]
for c in short_corners:
if s.endswith('_'+c):
found_corners.append(c)
s = s[:-2]
# Drain Extended
if 'defet' in s:
found_corners.append('extended_drain')
s = s.replace('defet', '')
if found_corners:
params_update(params, 'corners', found_corners)
if 'esd' not in params:
if 'esd' in s:
s = s.replace('esd', '')
params_update(params, 'esd', True)
else:
params_update(params, 'esd', False)
if 'rf2' in s:
params_update(params, 'rf', True)
s = s.replace('rf2', '_')
elif 'rf' in s:
params_update(params, 'rf', True)
s = s.replace('rf', '_')
else:
params_update(params, 'rf', False)
#if 'par' in s:
# s = s.replace('par', '_')
# params_update(params, 'parasitic', True)
while '__' in s:
s = s.replace('__', '_')
if s.startswith('_'):
s = s[1:]
if s.endswith('_'):
s = s[:-1]
return s, params
def params_update_multi(params, d):
for k in d:
params_update(params, k, d[k])
def acc(f):
"""
>>> acc(1.0)
0
>>> acc(1.3)
1
>>> acc(1.567)
3
"""
i = 0
while f != round(f, i):
i += 1
return i
def round_half_up(n, decimals=0):
multiplier = 10 ** decimals
return math.floor(n*multiplier + 0.5) / multiplier
def params_update(params, k, v):
"""
>>> p = {}
>>> p['k'] = 10.5
>>> params_update(p, 'k', 10.45)
>>> params_update(p, 'k', 11)
Traceback (most recent call last):
...
ValueError: Value for 'k' already exists.
Old: 10.45
New: 11
----
{'k': 10.45}
----
>>> p
{'k': 10.45}
>>> params_update(p, 'k', 'hello')
Traceback (most recent call last):
...
ValueError: Value for 'k' already exists.
Old: 10.45
New: 'hello'
----
{'k': 10.45}
----
>>> params_update(p, 'v', 'hello')
>>> p
{'k': 10.45, 'v': 'hello'}
>>> p['broken name?'] = True
>>> params_update(p, 'v', 'random')
>>> p
{'k': 10.45, 'v': 'hello', 'broken name?': True}
"""
dont_update = params.get('broken name?', False)
if dont_update and k in params:
return
if k not in params:
params[k] = v
return
ov = params[k]
if not isinstance(v, float):
eq_nv = v
eq_ov = ov
err_extra = '.'
err_nv = repr(v)
err_ov = repr(ov)
else:
a = min(acc(v), acc(ov))
eq_nv = round_half_up(v, a)
eq_ov = round_half_up(ov, a)
if acc(v) > acc(ov):
params[k] = v
err_extra = ' (Checking to {} after decimal point accuracy).'.format(a)
err_nv = "%-10r (%r)" % ( v, eq_nv)
err_ov = "%-10r (%r)" % (ov, eq_ov)
if eq_nv != eq_ov:
raise ValueError('''Value for {k} already exists{e}
Old: {ov}
New: {nv}
----
{p}
----'''.format(k=repr(k), e=err_extra, ov=err_ov, nv=err_nv, p=pformat(params)))
##########################################################################
# BJT decoding
##########################################################################
RE_BJT_SIZE = re.compile('_?(?P<x>[0-9]+(p[0-9]+)?)x(?P<y>[0-9]+(p[0-9]+)?)')
BJT_CUSTOM = {}
def csv_parse_bool(s):
if s in ('TRUE', '1'):
return True
elif s in ('FALSE', '0', ''):
return False
raise ValueError('Unknown boolean: '+repr(s))
with open('bjt_custom.csv', newline='') as f:
for r in csv.DictReader(f):
if not r['key']:
continue
if r['key'][0] == '#':
continue
try:
if not r['x']:
r['x'] = '???'
else:
r['x'] = float(r['x'])
if not r['y']:
r['y'] = '???'
else:
r['y'] = float(r['y'])
if r['width']:
r['width'] = float(r['width'])
else:
del r['width']
if r['length']:
r['length'] = float(r['length'])
else:
del r['length']
if not r['vcc']:
del r['vcc']
r['parasitic'] = csv_parse_bool(r['parasitic'])
r['esd'] = csv_parse_bool(r['esd'])
r['rf'] = csv_parse_bool(r['rf'])
if not r['props']:
r['props'] = []
else:
r['props'] = eval(r['props'])
if not r['variant']:
del r['variant']
k = r.pop('key')
assert k not in BJT_CUSTOM, assert_pformat(k, r, BJT_CUSTOM)
BJT_CUSTOM[k] = dict(r)
except ValueError as e:
print('bjt_custom.csv', r, 'ValuError:', e)
raise
def parse_bjt(s):
"""
* poly-gated version with octagonal emitter of A = 1.97 µm2 
>>> p('npn_1x1_2p0')
({'device': 'bjt',
'esd': False,
'length': 1.0,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '5v5',
'vrange': 'high',
'width': 1.0,
'x': 8.62,
'y': 8.62},
'')
('npn_05v5', 'sky130_fd_pr__npn_05v5_W1p00L1p00')
>>> p('npn_wafer')
({'corners': ['wafer'],
'device': 'bjt',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '5v5',
'vrange': 'high'},
'')
('npn_05v5', 'sky130_fd_pr__npn_05v5__wafer')
>>> p('npn')
({'device': 'bjt',
'esd': False,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'npn',
'variant': 'all',
'vcc': '5v5',
'vrange': 'high',
'x': 8.62,
'y': 8.62},
'')
('npn_05v5', 'sky130_fd_pr__npn_05v5_all')
* ungated device with emitter 1.0 x 1.0 
>>> p('npn_1x1')
({'device': 'bjt',
'esd': False,
'length': 1.0,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '5v5',
'vrange': 'high',
'width': 1.0,
'x': 8.62,
'y': 8.62},
'')
('npn_05v5', 'sky130_fd_pr__npn_05v5_W1p00L1p00')
>>> p('rf_npn_5x5')
({'device': 'bjt',
'esd': False,
'length': 5.0,
'parasitic': False,
'props': [],
'rf': True,
'subdevice': 'npn',
'vcc': '5v5',
'vrange': 'high',
'width': 5.0,
'x': 13.96,
'y': 13.96},
'')
('rf_npn_05v5', 'sky130_fd_pr__rf_npn_05v5_W5p00L5p00')
Parasitic NPN 5.5V npnpar1x1, npnpar1x2
* ungated device with emitter 1.0 x 2.0 
>>> p('npnpar1x2')
({'device': 'bjt',
'esd': False,
'length': 2.0,
'parasitic': True,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '5v5',
'vrange': 'high',
'width': 1.0,
'x': 8.62,
'y': 9.62},
'')
('npn_05v5', 'sky130_fd_pr__npn_05v5_W1p00L2p00')
Parasitic HV Gated NPN 11V npn_1x1_2p0_hv
The npn_1x1_2p0_hv device has a poly gate placed between the emitter and
base diffusions, to prevent carrier recombination at the STI edge and
increase β.  The poly gate is connected to the emitter terminal. 
>>> p('npn_1x1_2p0_hv')
({'device': 'bjt',
'esd': False,
'length': 1.0,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '11v0',
'vrange': 'very',
'width': 1.0,
'x': 9.75,
'y': 9.75},
'')
('npn_11v0', 'sky130_fd_pr__npn_11v0_W1p00L1p00')
>>> p('npnpar_polyhv')
({'device': 'bjt',
'esd': False,
'length': 1.0,
'parasitic': True,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '11v0',
'vrange': 'very',
'width': 1.0,
'x': 9.75,
'y': 9.75},
'')
('npn_11v0', 'sky130_fd_pr__npn_11v0_W1p00L1p00')
The following sizes of PNP are available:
* pnpar - ungated device with emitter 0.68 x 0.68 (A=0.4624 µm2) 
* pnppar5x ungated device with emitter 3.4 x 3.4 (A=11.56 µm2)
Parasitic PNP 5.5V pnppar, pnppar5x
>>> p('pnp')
({'device': 'bjt',
'esd': False,
'length': 0.68,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'pnp',
'vcc': '5v5',
'vrange': 'high',
'width': 0.68,
'x': 3.98,
'y': 3.98},
'')
('pnp_05v5', 'sky130_fd_pr__pnp_05v5_W0p68L0p68')
>>> p('pnppar')
({'device': 'bjt',
'esd': False,
'length': 0.68,
'parasitic': True,
'props': [],
'rf': False,
'subdevice': 'pnp',
'vcc': '5v5',
'vrange': 'high',
'width': 0.68,
'x': 3.98,
'y': 3.98},
'')
('pnp_05v5', 'sky130_fd_pr__pnp_05v5_W0p68L0p68')
>>> p('rf_pnp5x')
({'device': 'bjt',
'esd': False,
'length': 3.4,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'pnp',
'vcc': '5v5',
'vrange': 'high',
'width': 3.4,
'x': 6.7,
'y': 6.7},
'')
('pnp_05v5', 'sky130_fd_pr__pnp_05v5_W3p40L3p40')
sky130_fd_pr__pnp_05v5_W3p40L3p40
>>> p('s8rf_pnp5x')
({'device': 'bjt',
'esd': False,
'length': 3.4,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'pnp',
'vcc': '5v5',
'vrange': 'high',
'width': 3.4,
'x': 6.7,
'y': 6.7},
'')
('pnp_05v5', 'sky130_fd_pr__pnp_05v5_W3p40L3p40')
ESD Parasitic PNP 5.5V xpnppar
>>> p('xpnppar')
({'device': 'bjt',
'esd': True,
'parasitic': True,
'props': [],
'rf': False,
'subdevice': 'pnp',
'vcc': '5v5',
'vrange': 'high',
'x': '???',
'y': '???'},
'')
('esd_pnp_05v5', 'sky130_fd_pr__esd_pnp_05v5')
>>> p('pnp4')
({'device': 'bjt',
'esd': False,
'length': 0.68,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'pnp',
'vcc': '5v5',
'vrange': 'high',
'width': 0.68,
'x': 3.98,
'y': 3.98},
'')
('pnp_05v5', 'sky130_fd_pr__pnp_05v5_W0p68L0p68')
>>> p('npnpar1x1')
({'device': 'bjt',
'esd': False,
'length': 1.0,
'parasitic': True,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '5v5',
'vrange': 'high',
'width': 1.0,
'x': 8.62,
'y': 8.62},
'')
('npn_05v5', 'sky130_fd_pr__npn_05v5_W1p00L1p00')
>>> p('npn_1x2')
({'device': 'bjt',
'esd': False,
'length': 2.0,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '5v5',
'vrange': 'high',
'width': 1.0,
'x': 8.62,
'y': 9.62},
'')
('npn_05v5', 'sky130_fd_pr__npn_05v5_W1p00L2p00')
>>> p('npn_1x2rf')
({'device': 'bjt',
'esd': False,
'length': 2.0,
'props': [],
'rf': True,
'subdevice': 'npn',
'vcc': '5v5',
'vrange': 'high',
'width': 1.0},
'')
('rf_npn_05v5', 'sky130_fd_pr__rf_npn_05v5_W1p00L2p00')
>>> p('rf_npn_1x1_2p0_hv')
({'device': 'bjt',
'esd': False,
'length': 1.0,
'parasitic': False,
'props': [],
'rf': False,
'subdevice': 'npn',
'vcc': '11v0',
'vrange': 'very',
'width': 1.0,
'x': 9.75,
'y': 9.75},
'')
('npn_11v0', 'sky130_fd_pr__npn_11v0_W1p00L1p00')
#>>> p('ns_1p65p15m2_b')
"""
orig_s = s
params = {}
s = RE_S8RF.sub('\\1_', s)
if s in BJT_CUSTOM:
params_update_multi(params, BJT_CUSTOM[s])
# esd / rf / corners
s, cparams = parse_common(s)
if s.startswith('x'):
s = s[1:]
cparams['esd'] = True
if 'pnp' in orig_s or '_hv' in orig_s:
cparams['rf'] = False
params_update_multi(params, cparams)
if 'par' in s:
s = s.replace('par', '').strip('_')
params_update(params, 'device', 'bjt')
if s.startswith('pnp'):
params_update(params, 'subdevice', 'pnp')
s = s[3:]
elif s.startswith('npn'):
params_update(params, 'subdevice', 'npn')
s = s[3:]
else:
return None, s
m = RE_BJT_SIZE.search(s)
if m:
params_update(params, 'width', float(m.group('x').replace('p', '.')))
params_update(params, 'length', float(m.group('y').replace('p', '.')))
s = s[:m.start(0)]+s[m.end(0):]
if 'hv' in s:
s = s.replace('hv', '')
assert s in ("_2p0_", "_poly"), s
s = ''
params_update(params, 'vcc', '11v0')
params_update(params, 'vrange', 'very')
else:
params_update(params, 'vcc', '5v5')
params_update(params, 'vrange', 'high')
if 'props' not in params:
params['props'] = []
s = s.strip('_')
if s in ('4', '5x', '2p0'):
s = ''
return params, s
##########################################################################
# FET decoding
##########################################################################
FET_PROPERTIES = {
'base':'base',
'withptap':'with ptap',
'noptap': 'no ptap',
'iso1': 'iso',
'iso': 'iso',
'hbm': 'hbm', # Human Body Model
'iec': 'iec', # IEC ESD standard?
# FIXME: Figure out what these mean...
'aup': 'aup', # advanced ultra-low power?
'reverse':'reverse',
'extd': 'extend drain',
}
# _w3p0_l0p5_m10_b # Multiples + Variant
# _w3p0_l0p5_m4 # Multiples
# _w0p42_l0p15_8f # Fingers
#
RE_FET_WIDTH = re.compile(
'_w(?P<w>[0-9]+p[0-9]+)(_|$)'
)
RE_FET_LENGTH = re.compile(
'_l(?P<l>[0-9]+p[0-9]+)(_|$)'
)
RE_FET_MULTIPLE = re.compile(
'_m(?P<m>[0-9]+)'
)
RE_FET_FINGERS = re.compile(
'_(?P<f>[0-9]+)f'
)
# nf2 --> _f2
RE_OLD_F = re.compile(
'nf([0-9]+)(_|$)'
)
RE_SEPERATE_RF = re.compile(
'([^_])(rf2?)(_|$)'
)
RE_RF_NOT_FIRST = re.compile(
'^([^_8]+)_(rf)2?'
)
RE_S8RF = re.compile(
'^s8_?(rf)?2?_'
)
VOLTAGE_RANGES = {
# ultra >= 20.0V
'20v0' :('20', 'ultra'),
'g5v0d20v0' :('20', 'ultra'),
# 20.0V > very > 11.0V
'g11v0d16v0':('vhv', 'very' ),
'g5v0d16v0' :('vhv', 'very' ),
# 11.0V >= high > 1.8V
'g5v0d10v5' :('hv', 'high' ),
'g3v3d10v5' :('hv', 'high' ),
'5v0' :('hv', 'high' ),
'3v3' :('tv', 'high' ),
# 1.8V >= low
'1v8' :('', 'low' ),
}
def _cleanup_p(a, b):
"""
>>> _cleanup_p(None, None)
'0p00'
>>> _cleanup_p(None, '0')
'0p00'
>>> _cleanup_p('0', None)
'0p00'
>>> _cleanup_p('20', None)
'20p00'
>>> _cleanup_p('20', '1')
'20p10'
>>> _cleanup_p('20', '100')
'20p100'
>>> _cleanup_p('20', '100')
'20p100'
"""
if not a:
a = '0'
if not b:
b = ''
while len(b) < 2:
b += '0'
return '{}p{}'.format(a, b)
def cleanup_p(s):
"""
>>> cleanup_p(None)
'0p00'
>>> cleanup_p('p1')
'0p10'
>>> cleanup_p('1p')
'1p00'
>>> cleanup_p('p')
'0p00'
>>> cleanup_p('')
'0p00'
"""
if not s:
s = ''
p = s.split('p', 1)
if len(p) < 2:
p.append(None)
return _cleanup_p(*p)
RE_MULTI_P = [
re.compile('(?P<w>[0-9]*p[0-9]+)(?P<l>p[0-9]+)(?P<e>.*)'),
re.compile('(?P<w>[0-9]+)(?P<l>p[0-9]+)(?P<e>.*)'),
re.compile('w(?P<w>[0-9]*p[0-9]+)(?P<l>)(?P<e>.*)'),
re.compile('w(?P<w>[0-9]+(p[0-9]*)?)(?P<l>)(?P<e>.*)'),
re.compile('(?P<w>)l(?P<l>[0-9]*p[0-9]+)(?P<e>.*)'),
re.compile('(?P<w>)l(?P<l>[0-9]+(p[0-9]*)?)(?P<e>.*)'),
]
def cleanup_multi_p(s):
"""
>>> cleanup_multi_p('w5p00_l0p18_m2')
'w5p00_l0p18_m2'
>>> cleanup_multi_p('5p18m2')
'w5p00_l0p18_m2'
>>> cleanup_multi_p('w5p00_l0p25')
'w5p00_l0p25'
>>> cleanup_multi_p('w5_lp25')
'w5p00_l0p25'
>>> cleanup_multi_p('w5m5')
'w5p00_m5'
>>> cleanup_multi_p('w1p68_l0p15_nf2')
'w1p68_l0p15_nf2'
>>> cleanup_multi_p('1p68p15nf2')
'w1p68_l0p15_nf2'
>>> cleanup_multi_p('p84p15nf2')
'w0p84_l0p15_nf2'
>>> cleanup_multi_p('rf_nhv_base_b_tt_leak')
'rf_nhv_base_b_tt_leak'
>>> cleanup_multi_p('n20vhv_wafer_discrete')
'n20vhv_wafer_discrete'
"""
bits = s.split('_')
o = []
for b in bits:
m = None
for r in RE_MULTI_P:
m = r.match(b)
if not m:
continue
w = m.group('w')
if w:
o.append('w'+cleanup_p(w))
l = m.group('l')
if l:
o.append('l'+cleanup_p(l))
e = m.group('e')
if e:
o.append(e)
break
if not m:
o.append(b)
return '_'.join(o)
def cleanup_fet(s):
"""
>>> cleanup_fet('n20vh1defet')
'n20vhdefet'
>>> cleanup_fet('s8rf_pshort_W1p65_L0p25_M2')
's8_rf_pshort_w1p65_l0p25_m2'
>>> cleanup_fet('nsrf_1p65p15m2_b')
'rf_nshort_w1p65_l0p15_m2_b'
>>> cleanup_fet('rf_nhv_base_m4_b_w7')
'rf_nhv_m4_b_w7p00'
>>> cleanup_fet('rf_nlowvt_base_m4_b_w5_lp25')
'rf_nlowvt_m4_b_w5p00_l0p25'
>>> cleanup_fet('pmedlvtrf_1p68p15nf2')
'rf_pmedlvt_w1p68_l0p15_2f'
>>> cleanup_fet('pmedlvtrf2_1p68p15nf2')
'rf_pmedlvt_w1p68_l0p15_2f'
>>> cleanup_fet('pmedlvtrf_1p68m15nf2')
'rf_pmedlvt_w1p00_l0p68_m15_2f'
>>> cleanup_fet('nhvrf_3p50m10_b')
'rf_nhv_w3p00_l0p50_m10_b'
>>> cleanup_fet('pmedlvtrf_p84p15nf2')
'rf_pmedlvt_w0p84_l0p15_2f'
# nlowvt_rf.pm3
>>> cleanup_fet('nlrf_1p65p15m2_b')
'rf_nlowvt_w1p65_l0p15_m2_b'
# nshort_rf.pm3
>>> cleanup_fet('nsrf_1p65p15m2_b')
'rf_nshort_w1p65_l0p15_m2_b'
# pshort_rf.pm3 --> pshort_rf_base_m2_b_w5
# psrf_5p18m2_b (d g s b)
# pshort_rf_base_m2_b_w5
# w = 5.05 l = 0.18 m = 2
# w5p00 l0p18
>>> cleanup_fet('psrf_5p18m2_b')
'rf_pshort_w5p00_l0p18_m2_b'
# psrf_1p68p15nf2 (1 2 3 b) pshort l=0.15 w=(2)*(1.68)
>>> cleanup_fet('psrf_1p68p15nf2')
'rf_pshort_w1p68_l0p15_2f'
>>> cleanup_fet('pmedlvtrf_1p68m15_2f')
'rf_pmedlvt_w1p00_l0p68_m15_2f'
>>> cleanup_fet('rf_nhv_base_b_tt_leak')
'rf_nhv_b_tt_leak'
>>> cleanup_fet('rf_nlowvt_base_b_wafer')
'rf_nlowvt_b_wafer'
"""
s = s.lower()
if 'nsrf_' in s:
s = s.replace('nsrf_', 'rf_nshort_')
if 'nlrf_' in s:
s = s.replace('nlrf_', 'rf_nlowvt_')
if 'psrf_' in s:
s = s.replace('psrf_', 'rf_pshort_')
s = s.replace('_base_', '_')
s = cleanup_multi_p(s)
# nf2 --> _f2
s = RE_OLD_F.sub('_\\1f\\2', s)
# xxxrf_ --> xxx_rf_
s = RE_SEPERATE_RF.sub('\\1_\\2_\\3', s)
s = RE_RF_NOT_FIRST.sub('\\2_\\1', s)
# vhv1 --> vhv
# vh1 --> vh
s = re.sub('((?:vhv)|(?:vh))1', '\\1', s)
s = re.sub('_+', '_', s)
if s.startswith('_'):
s = s[1:]
if s.endswith('_'):
s = s[:-1]
return s
# FIXME: Decode these...
# Unable to decode s8phirs_10r,nfet
# Unable to decode s8phirs_10r,nfet_backup
# Unable to decode s8phirs_10r,nfet_debug
# Unable to decode s8phirs_10r,nfetextd
# Unable to decode s8phirs_10r,nfetextd_backup
# Unable to decode s8phirs_10r,nfetextd_old
# Unable to decode s8phirs_10r,nfetextd_old2
# Unable to decode s8phirs_10r,nfet_fixed
# Unable to decode s8phirs_10r,nfet_old
# Unable to decode s8phirs_10r,nfet_symbolic
# Unable to decode s8phirs_10r,pEsdCascodeFet
# Unable to decode s8phirs_10r,pEsdFet
# Unable to decode s8phirs_10r,pfet
# Unable to decode s8phirs_10r,pfet_backup
# Unable to decode s8phirs_10r,pfetextd
# Unable to decode s8phirs_10r,pfetextd_backup
# Unable to decode s8phirs_10r,pfetextd_old
# Unable to decode s8phirs_10r,pfet_fixed
# Unable to decode s8phirs_10r,pfet_symbolic
# Unable to decode s8phirs_10r,lvtrans2
# Unable to decode s8phirs_10r,lvtrans3
# Unable to decode s8phirs_10r,lvtrans4
def parse_fet(s):
"""
>>> p('n20vhvisoreverse1')
({'device': 'fet',
'esd': False,
'props': ['reverse', 'iso'],
'rf': False,
'subdevice': 'nfet',
'vcc': '20v0',
'vrange': 'ultra'},
'')
('nfet_20v0_reverse_iso', 'sky130_fd_pr__nfet_20v0_reverse_iso')
>>> p('nlowvt_w0p84_l0p15_4f')
({'device': 'fet',
'esd': False,
'fingers': 4,
'length': 0.15,
'props': [],
'rf': False,
'subdevice': 'nfet',
'variant': 'a',
'vcc': '1v8',
'vrange': 'low',
'vt': 'low',
'width': 0.84},
'')
('nfet_01v8_lvt', 'sky130_fd_pr__nfet_01v8_lvt_aF04W0p84L0p15')
>>> p('nlowvt_w0p84_l0p15_4frf')
({'device': 'fet',
'esd': False,
'fingers': 4,
'length': 0.15,
'props': [],
'rf': True,
'subdevice': 'nfet',
'variant': 'a',
'vcc': '1v8',
'vrange': 'low',
'vt': 'low',
'width': 0.84},
'')
('rf_nfet_01v8_lvt', 'sky130_fd_pr__rf_nfet_01v8_lvt_aF04W0p84L0p15')
>>> p('nhv_ff_discrete')
({'corners': ['ff', 'discrete'],
'device': 'fet',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'nfet',
'vcc': 'g5v0d10v5',
'vrange': 'high'},
'')
('nfet_g5v0d10v5', 'sky130_fd_pr__nfet_g5v0d10v5__ff_discrete')
>>> p('s8rf_pshort_W1p65_L0p25_M2')
({'device': 'fet',
'esd': False,
'length': 0.25,
'multiple': 2,
'props': [],
'rf': True,
'subdevice': 'pfet',
'variant': 'a',
'vcc': '1v8',
'vrange': 'low',
'width': 1.65},
'')
('rf_pfet_01v8', 'sky130_fd_pr__rf_pfet_01v8_aM02W1p65L0p25')
>>> p('nhvnativeesd')
({'device': 'fet',
'esd': True,
'props': [],
'rf': False,
'subdevice': 'nfet',
'vcc': '5v0',
'vrange': 'high',
'vt': 'native'},
'')
('esd_nfet_05v0_nvt', 'sky130_fd_pr__esd_nfet_05v0_nvt')
>>> p('n20nativevhv1_aup')
({'device': 'fet',
'esd': False,
'props': ['aup'],
'rf': False,
'subdevice': 'nfet',
'vcc': '20v0',
'vrange': 'ultra',
'vt': 'native'},
'')
('nfet_20v0_nvt_aup', 'sky130_fd_pr__nfet_20v0_nvt_aup')
>>> p('nhv_w3p0_lp5_m4')
({'device': 'fet',
'esd': False,
'length': 0.5,
'multiple': 4,
'props': [],
'rf': False,
'subdevice': 'nfet',
'variant': 'a',
'vcc': 'g5v0d10v5',
'vrange': 'high',
'width': 3.0},
'')
('nfet_g5v0d10v5', 'sky130_fd_pr__nfet_g5v0d10v5_aM04W3p00L0p50')
>>> p('nhv_w3p0_l0p5_m4_b')
({'device': 'fet',
'esd': False,
'length': 0.5,
'multiple': 4,
'props': [],
'rf': False,
'subdevice': 'nfet',
'variant': 'b',
'vcc': 'g5v0d10v5',
'vrange': 'high',
'width': 3.0},
'')
('nfet_g5v0d10v5', 'sky130_fd_pr__nfet_g5v0d10v5_bM04W3p00L0p50')
>>> p('n20vhv1_withptap')
({'device': 'fet',
'esd': False,
'props': ['with ptap'],
'rf': False,
'subdevice': 'nfet',
'vcc': '20v0',
'vrange': 'ultra'},
'')
('nfet_20v0_withptap', 'sky130_fd_pr__nfet_20v0_withptap')
>>> p('n20vhviso1_noptap')
({'device': 'fet',
'esd': False,
'props': ['no ptap', 'iso'],
'rf': False,
'subdevice': 'nfet',
'vcc': '20v0',
'vrange': 'ultra'},
'')
('nfet_20v0_noptap_iso', 'sky130_fd_pr__nfet_20v0_noptap_iso')
>>> p('nlowvt_w0p42_l0p15_4f')
({'device': 'fet',
'esd': False,
'fingers': 4,
'length': 0.15,
'props': [],
'rf': False,
'subdevice': 'nfet',
'variant': 'a',
'vcc': '1v8',
'vrange': 'low',
'vt': 'low',
'width': 0.42},
'')
('nfet_01v8_lvt', 'sky130_fd_pr__nfet_01v8_lvt_aF04W0p42L0p15')
>>> p('nlowvt_w3p0_l0p18_m2_c')
({'device': 'fet',
'esd': False,
'length': 0.18,
'multiple': 2,
'props': [],
'rf': False,
'subdevice': 'nfet',
'variant': 'c',
'vcc': '1v8',
'vrange': 'low',
'vt': 'low',
'width': 3.0},
'')
('nfet_01v8_lvt', 'sky130_fd_pr__nfet_01v8_lvt_cM02W3p00L0p18')
>>> p('nshort_w3p0_l0p15_m4_mc')
({'device': 'fet',
'esd': False,
'length': 0.15,
'multiple': 4,
'props': [],
'rf': False,
'subdevice': 'nfet',
'variant': 'mc',
'vcc': '1v8',
'vrange': 'low',
'width': 3.0},
'')
('nfet_01v8', 'sky130_fd_pr__nfet_01v8_mcM04W3p00L0p15')
>>> p('pshort_w3p0_l0p15_m4_hc')
({'device': 'fet',
'esd': False,
'length': 0.15,
'multiple': 4,
'props': [],
'rf': False,
'subdevice': 'pfet',
'variant': 'hc',
'vcc': '1v8',
'vrange': 'low',
'width': 3.0},
'')
('pfet_01v8', 'sky130_fd_pr__pfet_01v8_hcM04W3p00L0p15')
>>> p('n20vhv1_esd_hbm_21v_w60')
({'device': 'fet',
'esd': True,
'props': ['hbm'],
'rf': False,
'subdevice': 'nfet',
'variant': '21v',
'vcc': '20v0',
'vrange': 'ultra',
'width': 60.0},
'')
('esd_nfet_20v0_hbm', 'sky130_fd_pr__esd_nfet_20v0_hbm_21vW60p00')
>>> p('pmedlvtrf_1p68p15nf2')
({'device': 'fet',
'esd': False,
'fingers': 2,
'length': 0.15,
'props': [],
'rf': True,
'subdevice': 'pfet',
'variant': 'a',
'vcc': '1v8',
'vrange': 'low',
'vt': 'med',
'width': 1.68},
'')
('rf_pfet_01v8_mvt', 'sky130_fd_pr__rf_pfet_01v8_mvt_aF02W1p68L0p15')
>>> p('pmedlvtrf_1p68m15_2f')
({'device': 'fet',
'esd': False,
'fingers': 2,
'length': 0.68,
'multiple': 15,
'props': [],
'rf': True,
'subdevice': 'pfet',
'variant': 'a',
'vcc': '1v8',
'vrange': 'low',
'vt': 'med',
'width': 1.0},
'')
('rf_pfet_01v8_mvt', 'sky130_fd_pr__rf_pfet_01v8_mvt_aM15F02W1p00L0p68')
>>> p('nhv_rf_base_m4_b_w7')
({'device': 'fet',
'esd': False,
'multiple': 4,
'props': [],
'rf': True,
'subdevice': 'nfet',
'variant': 'b',
'vcc': 'g5v0d10v5',
'vrange': 'high',
'width': 7.0},
'')
('rf_nfet_g5v0d10v5', 'sky130_fd_pr__rf_nfet_g5v0d10v5_bM04W7p00')
>>> p('nhv_rf_base_b_tt_leak')
({'corners': ['tt', 'leak'],
'device': 'fet',
'esd': False,
'props': [],
'rf': True,
'subdevice': 'nfet',
'variant': 'b',
'vcc': 'g5v0d10v5',
'vrange': 'high'},
'')
('rf_nfet_g5v0d10v5', 'sky130_fd_pr__rf_nfet_g5v0d10v5_b__tt_leak')
>>> p('nlowvt_rf_base_m4_b_w5_lp25')
({'device': 'fet',
'esd': False,
'length': 0.25,
'multiple': 4,
'props': [],
'rf': True,
'subdevice': 'nfet',
'variant': 'b',
'vcc': '1v8',
'vrange': 'low',
'vt': 'low',
'width': 5.0},
'')
('rf_nfet_01v8_lvt', 'sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p25')
>>> p('nsrf')
({'basename': 'rf_nfet_01v8',
'device': 'special',
'esd': False,
'group': 'nfet',
'key': 'nsrf',
'props': [],
'rf': True,
'typename': 'sky130_fd_pr__rf_nfet_01v8'},
'')
('rf_nfet_01v8', 'sky130_fd_pr__rf_nfet_01v8')
>>> p('nlrf')
({'basename': 'rf_nfet_01v8_lvt',
'device': 'special',
'esd': False,
'group': 'nfet',
'key': 'nlrf',
'props': [],
'rf': True,
'typename': 'sky130_fd_pr__rf_nfet_01v8_lvt'},
'')
('rf_nfet_01v8_lvt', 'sky130_fd_pr__rf_nfet_01v8_lvt')
>>> p('psrf')
({'basename': 'rf_pfet_01v8',
'device': 'special',
'esd': False,
'group': 'pfet',
'key': 'psrf',
'props': [],
'rf': True,
'typename': 'sky130_fd_pr__rf_pfet_01v8'},
'')
('rf_pfet_01v8', 'sky130_fd_pr__rf_pfet_01v8')
w=5p00 and l=0p25, m=4
w5p00_l0p25_m4_b
>>> p('nsrf_5p25m4_b')
({'device': 'fet',
'esd': False,
'length': 0.25,
'multiple': 4,
'props': [],
'rf': True,
'subdevice': 'nfet',
'variant': 'b',
'vcc': '1v8',
'vrange': 'low',
'width': 5.0},
'')
('rf_nfet_01v8', 'sky130_fd_pr__rf_nfet_01v8_bM04W5p00L0p25')
w=1p65 and l=0p15, m=2
w1p65_l0p15_m2_b
>>> p('nsrf_1p65p15m2_b')
({'device': 'fet',
'esd': False,
'length': 0.15,
'multiple': 2,
'props': [],
'rf': True,
'subdevice': 'nfet',
'variant': 'b',
'vcc': '1v8',
'vrange': 'low',
'width': 1.65},
'')
('rf_nfet_01v8', 'sky130_fd_pr__rf_nfet_01v8_bM02W1p65L0p15')
>>> p('extdntran_180131884')
({'basename': 'nfet',
'device': 'special',
'esd': False,
'example': '180131884',
'group': 'model',
'key': 'extdntran',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__nfet_extendeddrain'},
'')
('nfet', 'sky130_fd_pr__model__nfet_extendeddrain__example1')
>>> p('extdptran_180133932')
({'basename': 'pfet',
'device': 'special',
'esd': False,
'example': '180133932',
'group': 'model',
'key': 'extdptran',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__nfet_extendeddrain'},
'')
('pfet', 'sky130_fd_pr__model__nfet_extendeddrain__example2')
>>> p('ntran_175311916')
({'basename': 'nfet',
'device': 'special',
'esd': False,
'example': '175311916',
'group': 'model',
'key': 'ntran',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__nfet'},
'')
('nfet', 'sky130_fd_pr__model__nfet__example1')
>>> p('nfet_173914156')
({'device': 'fet',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'nfet',
'vcc': '1v8',
'vrange': 'low'},
'_173914156')
('nfet_01v8', 'sky130_fd_pr__nfet_01v8')
>>> p('pfet_168833068')
({'device': 'fet',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'pfet',
'vcc': '1v8',
'vrange': 'low'},
'_168833068')
('pfet_01v8', 'sky130_fd_pr__pfet_01v8')
>>> p('s8rf_n20vhv1_esd_IEC_21V_W60')
({'device': 'fet',
'esd': True,
'props': ['iec'],
'rf': True,
'subdevice': 'nfet',
'variant': '21v',
'vcc': '20v0',
'vrange': 'ultra',
'width': 60.0},
'')
('esd_rf_nfet_20v0_iec', 'sky130_fd_pr__esd_rf_nfet_20v0_iec_21vW60p00')
>>> p('n20zvtvh1defet')
({'corners': ['extended_drain'],
'device': 'fet',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'nfet',
'vcc': '20v0',
'vrange': 'ultra',
'vt': 'zero'},
'')
('nfet_20v0_zvt', 'sky130_fd_pr__nfet_20v0_zvt__extended_drain')
>>> p('ntran')
({'basename': 'nfet',
'device': 'special',
'esd': False,
'group': 'model',
'key': 'ntran',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__nfet'},
'')
('nfet', 'sky130_fd_pr__model__nfet')
>>> p('extdntran')
({'basename': 'nfet',
'device': 'special',
'esd': False,
'group': 'model',
'key': 'extdntran',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__nfet_extendeddrain'},
'')
('nfet', 'sky130_fd_pr__model__nfet_extendeddrain')
"""
orig_s = s
params = {}
s = cleanup_fet(s)
# esd / rf / corners
s, cparams = parse_common(s)
params_update_multi(params, cparams)
params_update(params, 'device', 'fet')
# Random property extraction
props = []
for i in sorted(FET_PROPERTIES, key=lambda x: (-len(x), x)):
if i+'_' in s:
s = s.replace(i+'_', '_')
props.append(FET_PROPERTIES[i])
if '_'+i in s:
s = s.replace('_'+i, '_')
props.append(FET_PROPERTIES[i])
if i in s:
s = s.replace(i, '')
props.append(FET_PROPERTIES[i])
params_update(params, 'props', props)
while '__' in s:
s = s.replace('__', '_')
if not s:
return None, orig_s
elif s[0] == 'n':
params_update(params, 'subdevice', 'nfet')
elif s[0] == 'p':
params_update(params, 'subdevice', 'pfet')
else:
return None, orig_s
# FET matching
value = {
'fet': {'vcc': '1v8' , },
# Low Voltage NMOS 1.8 nshort
# Low Voltage PMOS 1.8 pshort
'short': {'vcc': '1v8' , },
# Low Voltage low-VT NMOS 1.8 nlowvt
# Low Voltage low-VT PMOS 1.8 plowvt
'lowvt': {'vcc': '1v8' , 'vt': 'low' , },
# Low Voltage med-VT PMOS 1.8 pmedvt
'medvt': {'vcc': '1v8' , 'vt': 'med' , },
'medlvt': {'vcc': '1v8' , 'vt': 'med' , },
# Low Voltage high-VT PMOS 1.8 phighvt
'highvt': {'vcc': '1v8' , 'vt': 'high' , },
# High Voltage NMOS 5.0/10.5 nhv
# High Voltage PMOS 5.0/10.5 phv
'hv': {'vcc': 'g5v0d10v5', },
# Special case!!!!
# HV ESD NMOS 5.0 nhvesd
# HV ESD PMOS 5.0 phvesd
'hvesd': {'vcc': '5v0' , },
# HV native NMOS 3.0 ntvnative
'tvnative': {'vcc': '3v3' , 'vt': 'native', },
# HV native NMOS 5.0 nhvnative
# HV native ESD NMOS 5.0 nhvnativeesd
'hvnative': {'vcc': '5v0' , 'vt': 'native', },
# VHV nmos 5/16V DE G-5 / D-16 nvhv
# VHV pmos 5/16V DE G-5 / D-16 pvhv
'vhv': {'vcc': 'g5v0d16v0', },
# UHV nmos 5/20V DE G-5 / D-20 n20vhv1
# UHV iso nmos 5/20V DE G-5 / D-20 n20vhviso1
# UHV pmos 5/20V DE G-5 / D-20 p20vhv1
'20vh': {'vcc': '20v0' , },
'20vhv': {'vcc': '20v0' , },
'20vhv1': {'vcc': '20v0' , },
# UHV pmos 5/16V DE G-5 / D-20 n20nativevhv1
'20nativevh': {'vcc': '20v0' , 'vt': 'native', },
'20nativevhv': {'vcc': '20v0' , 'vt': 'native', },
'20nativevhv1': {'vcc': '20v0' , 'vt': 'native', },
# UHV pmos 5/16V DE G-5 / D-20 n20nzvtvhv1
'20zvtvh': {'vcc': '20v0' , 'vt': 'zero' , },
'20zvtvhv': {'vcc': '20v0' , 'vt': 'zero' , },
'20zvtvhv1': {'vcc': '20v0' , 'vt': 'zero' , },
#'pass': {'vcc': '1v8' , 'type': 'pass' },
#'lvtpass': {'vcc': '1v8' , 'vt': 'low' , 'type': 'pass' },
}
match = None
# Special case the [np]hvesd as it has different VCC to [np]hv
if s[1:].startswith('hv_') and params['esd']:
match = 'hv'
params_update_multi(params, value['hvesd'])
else:
for k in sorted(value, key=lambda x: (-len(x), x)):
if s[1:].startswith(k):
assert match is None, (s, match, k)
match = k
params_update_multi(params, value[k])
break
assert match is not None, repr(s)
s = s[len(match)+1:]
vrange_check, vrange_value = VOLTAGE_RANGES[params['vcc']]
assert vrange_check in match, (vrange_check, match, vrange_value)
params_update(params, 'vrange', vrange_value)
variant = None
m = RE_FET_WIDTH.search(s)
if m:
variant = 'a'
params_update(params, 'width', float(m.group('w').replace('p', '.')))
s = s[:m.start(0)]+'_'+s[m.end(0):]
m = RE_FET_LENGTH.search(s)
if m:
variant = 'a'
params_update(params, 'length', float(m.group('l').replace('p', '.')))
s = s[:m.start(0)]+'_'+s[m.end(0):]
#m = RE_FET_MULTIPLE.search(s)
#if m:
# params_update(params, 'point', float(m.group('p').replace('p', '.')))
# params_update(params, 'multiple', int(m.group('m')))
# s = s[:m.start(0)]+s[m.end(0):]
m = RE_FET_MULTIPLE.search(s)
if m:
variant = 'a'
multiple = m.group('m')
if multiple:
params_update(params, 'multiple', int(multiple))
s = s[:m.start(0)]+s[m.end(0):]
m = RE_FET_FINGERS.search(s)
if m:
variant = 'a'
fingers = m.group('f')
if fingers:
params_update(params, 'fingers', int(fingers))
s = s[:m.start(0)]+s[m.end(0):]
while s.endswith('_'):
s = s[:-1]
for v in ('_b', '_c', '_hc', '_mc', '_21v', '_32v'):
if s.endswith(v):
variant = s[-len(v)+1:]
s = s[:-len(v)]
if variant:
params_update(params, 'variant', variant)
return params, s
# Voltage
# -------------------------------------------
# value
# voltage level
# vt detection
VT_LEVELS = {
'lowvt': ('vt', 'low' ),
'lvt': ('vt', 'low' ),
'medvt': ('vt', 'med' ),
'medlvt': ('vt', 'med' ),
'native': ('vt', 'native'),
}
##########################################################################
# Capacitor decoding
##########################################################################
RE_CAP_START = re.compile(
'xcmvppx?([0-9](_|$))?_?'
)
RE_CAP_PARAMS = re.compile(
'(?P<x>[0-9]+p[0-9]+)x(?P<y>[0-9]+p[0-9]+)'
'(?:_(?P<f>[^_s]*)_)?'
'(?:_?(?P<shield>.*?shield))?'
'(?:_?(?P<v>[0-9]*))?'
'(?:_?(?P<t>(?:raphael)|(?:top)|(?:subcell)|(?:nwell))?)'
)
RE_CAP_HD5_PARAMS = re.compile(
'hd5_'
'(?:'
'(?:(?P<x>[0-9]+(?:p[0-9])?)x(?P<y>[0-9]+(?:p[0-9])?))'
'|'
'(?:atlas_(?P<type>(?:fingercap)|(?:wafflecap))(?P<v>[0-9]+)?(?:_l(?P<l>[0-9]+))?)'
')'
# '(?:_(?P<m>[m0-9]+))?'
)
CAP_CUSTOM = {}
with open('cap_custom.csv', newline='') as f:
for r in csv.DictReader(f):
if not r['key']:
continue
if r['key'][0] == '#':
continue
try:
r['metal'] = csv_parse_layers('metal', r['metal'])
r['shield'] = csv_parse_layers('shield', r['shield'])
r['float'] = csv_parse_layers('float', r['float'])
if r['x']:
r['x'] = float(r['x'])
else:
del r['x']
if r['y']:
r['y'] = float(r['y'])
else:
del r['y']
if not r['broken name?']:
del r['broken name?']
else:
assert r['broken name?'] == 'TRUE', (r['broken name?'], r)
r['broken name?'] = True
if not r['props']:
r['props'] = []
else:
r['props'] = eval(r['props'])
if not r['variant']:
del r['variant']
if not r['vt']:
del r['vt']
k = r.pop('key')
assert k not in CAP_CUSTOM, assert_pformat(k, r, CAP_CUSTOM)
CAP_CUSTOM[k] = dict(r)
except ValueError as e:
print('cap_custom.csv', r, 'ValuError:', e)
raise
for k in list(CAP_CUSTOM.keys()):
if 'rf_' in k or 'rf2_' in k:
continue
nk1 = 'rf_'+k
nk2 = 'rf2_'+k
if nk1 not in CAP_CUSTOM:
CAP_CUSTOM[nk1] = dict(CAP_CUSTOM[k])
if nk2 not in CAP_CUSTOM:
CAP_CUSTOM[nk2] = dict(CAP_CUSTOM[k])
def parse_cap(s):
"""
>>> p('xcmvpp6p8x6p1_m1m4_fom')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': [],
'rf': False,
'shield': [],
'variant': 'fom',
'x': 6.8,
'y': 6.09},
'')
('cap_vpp_06p8x06p1_m1m2m3m4_noshield', 'sky130_fd_pr__cap_vpp_06p8x06p1_m1m2m3m4_noshield_fom')
>>> p('xcmvpp_hd5_atlas_wafflecap')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'waffle'],
'rf': False,
'shield': ['li']},
'')
('cap_vpp_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_m1m2m3m4_shieldl1_wafflecap')
>>> p('xcmvpp_atlas')
({'basename': 'capacitors',
'device': 'special',
'esd': False,
'group': 'vppcap',
'key': 'xcmvpp_atlas',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__cap_vpp_finger'},
'')
('capacitors', 'sky130_fd_pr__model__cap_vpp_finger')
>>> p('xcnwvc_top')
({'basename': 'capacitors',
'device': 'special',
'esd': False,
'group': 'var',
'key': 'xcnwvc_top',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__cap_var'},
'')
('capacitors', 'sky130_fd_pr__model__cap_var')
>>> p('xcmvpp_top')
({'basename': 'capacitors',
'device': 'special',
'esd': False,
'group': 'vppcap',
'key': 'xcmvpp_top',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__cap_vpp'},
'')
('capacitors', 'sky130_fd_pr__model__cap_vpp')
>>> p('xcmvpp')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li', 'm1', 'm2'],
'props': [],
'rf': False,
'shield': [],
'variant': 'old1',
'x': 8.58,
'y': 7.84},
'')
('cap_vpp_08p6x07p8_l1m1m2_noshield', 'sky130_fd_pr__cap_vpp_08p6x07p8_l1m1m2_noshield_o1')
>>> p('xcmvpp_2')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li', 'm1', 'm2'],
'props': [],
'rf': False,
'shield': [],
'variant': 'old1',
'x': 4.38,
'y': 4.59},
'')
('cap_vpp_04p4x04p6_l1m1m2_noshield', 'sky130_fd_pr__cap_vpp_04p4x04p6_l1m1m2_noshield_o1')
>>> p('xcmvpp11p5x11p7_m3')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3'],
'props': [],
'rf': False,
'shield': [],
'x': 11.45,
'y': 11.69},
'')
('cap_vpp_11p5x11p7_m1m2m3_noshield', 'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_noshield')
>>> p('xcmvpp6p8x6p_polym4shield')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li', 'm1', 'm2', 'm3'],
'props': [],
'rf': False,
'shield': ['poly', 'm4'],
'x': 6.84,
'y': 6.13},
'6p8x6p_polym4shield')
('cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4', 'sky130_fd_pr__cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4')
>>> p('xcmvpp_hd5')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['poly',
'li',
'm1',
'm2',
'm3',
'm4',
'm5'],
'props': [],
'rf': False,
'shield': [],
'variant': 'base',
'x': 11.45,
'y': 11.73},
'')
('cap_vpp_11p5x11p7_pol1m1m2m3m4m5_noshield', 'sky130_fd_pr__cap_vpp_11p5x11p7_pol1m1m2m3m4m5_noshield_base')
>>> p('xcmvpp_hd5_atlas_fingercap2')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'finger'],
'rf': False,
'shield': ['li'],
'variant': 'base2',
'x': 2.85,
'y': 6.1},
'')
('cap_vpp_02p9x06p1_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_basefingercap2')
>>> p('xcmimc')
({'basename': 'capacitors',
'device': 'special',
'esd': False,
'group': 'mim',
'key': 'xcmimc',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__cap_mim'},
'')
('capacitors', 'sky130_fd_pr__model__cap_mim')
>>> p('xcmimc1')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'mim',
'metal': ['m3'],
'props': [],
'rf': False,
'shield': [],
'variant': '1'},
'')
('cap_mim_m3', 'sky130_fd_pr__cap_mim_m3_1')
>>> p('xcmimc2')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'mim',
'metal': ['m3'],
'props': [],
'rf': False,
'shield': [],
'variant': '2'},
'')
('cap_mim_m3', 'sky130_fd_pr__cap_mim_m3_2')
>>> p('xcmim2c')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'mim',
'metal': ['m4'],
'props': [],
'rf': False,
'shield': []},
'')
('cap_mim_m4', 'sky130_fd_pr__cap_mim_m4')
>>> p('mimcap34')
({'corners': ['base'],
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'mim',
'metal': ['m3'],
'props': [],
'rf': False,
'shield': []},
'')
('cap_mim_m3', 'sky130_fd_pr__cap_mim_m3__base')
>>> p('xcnwvc')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'var',
'metal': [],
'props': [],
'rf': False,
'shield': [],
'vt': 'low'},
'')
('cap_var_lvt', 'sky130_fd_pr__cap_var_lvt')
>>> p('vpp_nhvnative10x4')
({'broken name?': True,
'corners': ['base'],
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li',
'm1',
'm2',
'm3',
'm4'],
'props': ['nhv', '10x4'],
'rf': False,
'shield': ['m5'],
'x': 11.34,
'y': 11.76},
'')
('cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5', 'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhv__base')
>>> p('xcmvpp5')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2'],
'props': [],
'rf': False,
'shield': [],
'x': 2.42,
'y': 4.59},
'')
('cap_vpp_02p4x04p6_m1m2_noshield', 'sky130_fd_pr__cap_vpp_02p4x04p6_m1m2_noshield')
>>> p('xcmvpp11p5x11p7_polym5shield')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li',
'm1',
'm2',
'm3',
'm4'],
'props': [],
'rf': False,
'shield': ['poly', 'm5'],
'x': 11.45,
'y': 11.73},
'')
('cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5', 'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5')
>>> p('s8rf2_xcmvpp_hd5_atlas_wafflecap1')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'l': 1,
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'waffle'],
'rf': True,
'shield': ['li'],
'x': 11.33,
'y': 11.33},
'')
('cap_vpp_11p3x11p3_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_11p3x11p3_m1m2m3m4_shieldl1_wafflecap')
>>> p('xcmvppx4_2xnhvnative10x4_top')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li',
'm1',
'm2',
'm3',
'm4'],
'props': ['nhv', '2x', '10x4'],
'rf': False,
'shield': ['m5'],
'variant': 'top',
'x': 11.34,
'y': 11.76},
'')
('cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5', 'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhvtop')
>>> p('xcmvppx4_2xnhvnative10x4_raphael')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li',
'm1',
'm2',
'm3',
'm4'],
'props': ['nhv', '2x', '10x4'],
'rf': False,
'shield': ['m5'],
'variant': 'raphael',
'x': 11.34,
'y': 11.76},
'')
('cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5', 'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhvraphael')
>>> p('xcmvppx4_2xnhvnative10x4_noextrafingers_raphael')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2'],
'props': ['nhv', '2x', '10x4'],
'rf': False,
'shield': [],
'variant': 'raphael2',
'x': 11.34,
'y': 11.76},
'')
('cap_vpp_11p3x11p8_m1m2_noshield', 'sky130_fd_pr__cap_vpp_11p3x11p8_m1m2_noshield_nhv2raphael')
>>> p('s8rf2_xcmvpp11p5x11p7_lim5shield')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': [],
'rf': True,
'shield': ['li', 'm5'],
'x': 11.45,
'y': 11.69},
'')
('cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5', 'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5')
>>> p('s8rf2_xcmvpp11p5x11p7_polym50p4shield')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li',
'm1',
'm2',
'm3',
'm4'],
'props': [],
'rf': True,
'shield': ['poly', 'm5'],
'variant': 'rcx',
'x': 11.5,
'y': 11.7},
'')
('cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5', 'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5_x')
>>> p('rf_xcmvpp1p8x1p8_m3shield')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': ['m3'],
'group': 'vppcap',
'metal': ['m1', 'm2'],
'props': [],
'rf': True,
'shield': ['li'],
'x': 3.88,
'y': 3.88},
'')
('cap_vpp_03p9x03p9_m1m2_shieldl1_floatm3', 'sky130_fd_pr__cap_vpp_03p9x03p9_m1m2_shieldl1_floatm3')
>>> p('xcmvpp11p5x11p7')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li', 'm1', 'm2'],
'props': [],
'rf': False,
'shield': [],
'x': 11.45,
'y': 11.73},
'')
('cap_vpp_11p5x11p7_l1m1m2_noshield', 'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2_noshield')
>>> p('xcmvpp11p5x11p7_m5shield')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li',
'm1',
'm2',
'm3',
'm4'],
'props': [],
'rf': False,
'shield': ['m5'],
'x': 11.45,
'y': 11.69},
'')
('cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5', 'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5')
>>> p('xcmvpp11p5x11p7_lim5shield')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': [],
'rf': False,
'shield': ['li', 'm5'],
'x': 11.45,
'y': 11.69},
'')
('cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5', 'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5')
>>> p('xcmvpp4p4x4p6_m3_lim5shield')
({'device': 'capacitor',
'esd': False,
'float': ['m4'],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3'],
'props': [],
'rf': False,
'shield': ['li', 'm5'],
'x': 4.38,
'y': 4.59},
'')
('cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4', 'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4')
>>> p('xcmvpp8p6x7p9_m3_lim5shield')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': ['m4'],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3'],
'props': [],
'rf': False,
'shield': ['li', 'm5'],
'x': 8.58,
'y': 7.84},
'')
('cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4', 'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4')
>>> p('xcmvpp11p5x11p7_m3_lim5shield')
({'device': 'capacitor',
'esd': False,
'float': ['m4'],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3'],
'props': [],
'rf': False,
'shield': ['li', 'm5'],
'x': 11.45,
'y': 11.69},
'')
('cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4', 'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4')
>>> p('xcmvpp11p5x11p7_m4shield')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li', 'm1', 'm2', 'm3'],
'props': [],
'rf': False,
'shield': ['m4'],
'x': 11.45,
'y': 11.69},
'')
('cap_vpp_11p5x11p7_l1m1m2m3_shieldm4', 'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3_shieldm4')
>>> p('xcmvpp6p8x6p1_polym4shield')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li', 'm1', 'm2', 'm3'],
'props': [],
'rf': False,
'shield': ['poly', 'm4'],
'x': 6.84,
'y': 6.13},
'')
('cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4', 'sky130_fd_pr__cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4')
>>> p('xcmvpp6p8x6p1_lim4shield')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3'],
'props': [],
'rf': False,
'shield': ['li', 'm4'],
'x': 6.8,
'y': 6.09},
'')
('cap_vpp_06p8x06p1_m1m2m3_shieldl1m4', 'sky130_fd_pr__cap_vpp_06p8x06p1_m1m2m3_shieldl1m4')
>>> p('xcmvpp2_phv5x4')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2'],
'props': ['phv', '5x4'],
'rf': False,
'shield': [],
'variant': 'old1',
'x': 4.38,
'y': 4.59},
'')
('cap_vpp_04p4x04p6_m1m2_noshield', 'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o1phv')
>>> p('xcmvpp_hd5_1x1')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['poly',
'li',
'm1',
'm2',
'm3',
'm4',
'm5'],
'props': [],
'rf': False,
'shield': [],
'x': 11.45,
'y': 11.73},
'')
('cap_vpp_11p5x11p7_pol1m1m2m3m4m5_noshield', 'sky130_fd_pr__cap_vpp_11p5x11p7_pol1m1m2m3m4m5_noshield')
>>> p('xcmvpp_hd5_1x2')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['poly',
'li',
'm1',
'm2',
'm3',
'm4',
'm5'],
'props': [],
'rf': False,
'shield': [],
'x': 11.45,
'y': 23.09},
'')
('cap_vpp_11p5x23p1_pol1m1m2m3m4m5_noshield', 'sky130_fd_pr__cap_vpp_11p5x23p1_pol1m1m2m3m4m5_noshield')
>>> p('xcmvpp_hd5_6p8x6p1_m1m4')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': [],
'rf': False,
'shield': [],
'x': 6.8,
'y': 6.1},
'')
('cap_vpp_06p8x06p1_m1m2m3m4_noshield', 'sky130_fd_pr__cap_vpp_06p8x06p1_m1m2m3m4_noshield')
>>> p('xcmvpp_hd5_5x2_testcase')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['poly',
'li',
'm1',
'm2',
'm3',
'm4',
'm5'],
'props': [],
'rf': False,
'shield': [],
'variant': 'testcase',
'x': 55.77,
'y': 23.09},
'')
('cap_vpp_55p8x23p1_pol1m1m2m3m4m5_noshield', 'sky130_fd_pr__cap_vpp_55p8x23p1_pol1m1m2m3m4m5_noshield_test')
>>> p('xcmvpp_hd5_atlas_fingercap_l5')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'finger'],
'rf': False,
'shield': ['li'],
'x': 2.7,
'y': 6.1},
'')
('cap_vpp_02p7x06p1_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_02p7x06p1_m1m2m3m4_shieldl1_fingercap')
>>> p('xcmvpp_hd5_atlas_fingercap2_l5')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'finger'],
'rf': False,
'shield': ['li'],
'variant': '2',
'x': 2.85,
'y': 6.1},
'')
('cap_vpp_02p9x06p1_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_fingercap2')
>>> p('xcmvpp_hd5_atlas_fingercap_l10')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'finger'],
'rf': False,
'shield': ['li'],
'x': 2.7,
'y': 11.1},
'')
('cap_vpp_02p7x11p1_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_02p7x11p1_m1m2m3m4_shieldl1_fingercap')
>>> p('xcmvpp_hd5_atlas_fingercap_l20')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'finger'],
'rf': False,
'shield': ['li'],
'x': 2.7,
'y': 21.1},
'')
('cap_vpp_02p7x21p1_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_02p7x21p1_m1m2m3m4_shieldl1_fingercap')
>>> p('xcmvpp_hd5_atlas_fingercap_l40')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'finger'],
'rf': False,
'shield': ['li'],
'x': 2.7,
'y': 41.1},
'')
('cap_vpp_02p7x41p1_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_02p7x41p1_m1m2m3m4_shieldl1_fingercap')
>>> p('xcmvpp_hd5_atlas_wafflecap1')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'l': 1,
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'waffle'],
'rf': False,
'shield': ['li'],
'x': 11.33,
'y': 11.33},
'')
('cap_vpp_11p3x11p3_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_11p3x11p3_m1m2m3m4_shieldl1_wafflecap')
>>> p('xcmvpp11p5x11p7_m1m2_raphael')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2'],
'props': [],
'rf': False,
'shield': [],
'variant': 'raphael',
'x': 11.5,
'y': 11.69},
'')
('cap_vpp_11p5x11p7_m1m2_noshield', 'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_noshield_raphael')
>>> p('xcmvpp11p5x11p7_m1m4m5shield_raphael')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': [],
'rf': False,
'shield': ['m5'],
'variant': 'raphael',
'x': 11.45,
'y': 11.69},
'')
('cap_vpp_11p5x11p7_m1m2m3m4_shieldm5', 'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldm5_raphael')
>>> p('xcmvpp5_raphael')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['m1', 'm2'],
'props': [],
'rf': False,
'shield': [],
'variant': 'raphael',
'x': 2.42,
'y': 4.59},
'')
('cap_vpp_02p4x04p6_m1m2_noshield', 'sky130_fd_pr__cap_vpp_02p4x04p6_m1m2_noshield_raphael')
>>> p('xcmvpp11p5x11p7_polym5modshield_raphael')
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li',
'm1',
'm2',
'm3',
'm4'],
'props': [],
'rf': False,
'shield': ['poly', 'm5'],
'variant': 'm5mod',
'x': 11.45,
'y': 11.73},
'')
('cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5', 'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5_m5pullin')
>>> p('rf2_xcmvpp_hd5_atlas_fingercap2_l5')
({'device': 'capacitor',
'esd': False,
'float': [],
'group': 'cap_int3',
'metal': ['m1', 'm2', 'm3', 'm4'],
'props': ['atlas', 'finger'],
'rf': True,
'shield': ['li'],
'variant': '2',
'x': 2.85,
'y': 6.1},
'')
('cap_vpp_02p9x06p1_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_fingercap2')
>>> p("rf2_xcmvppx4_2xnhvnative10x4")
({'broken name?': True,
'device': 'capacitor',
'esd': False,
'float': [],
'group': 'vppcap',
'metal': ['li',
'm1',
'm2',
'm3',
'm4'],
'props': ['nhv', '2x', '10x4'],
'rf': True,
'shield': ['m5'],
'x': 11.34,
'y': 11.76},
'')
('cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5', 'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhv')
"""
orig_s = s
params = {}
s = RE_S8RF.sub('\\1_', s)
custom = False
if s in CAP_CUSTOM:
custom = True
params_update_multi(params, CAP_CUSTOM[s])
# esd / rf / corners
s, cparams = parse_common(s)
params_update_multi(params, cparams)
params_update(params, 'device', 'capacitor')
if s in ("xcmvpp", "xcmvpp_2"):
return params, ''
if not s.startswith('xc'):
if 'corners' not in params:
params['corners'] = []
params['corners'].append('base')
m = RE_CAP_START.search(s)
if not m:
if custom:
return params, ''
else:
return {}, orig_s
s = s[m.end(0):]
if 'hd5' in s or 'atlas' in s:
m = RE_CAP_HD5_PARAMS.search(s)
if not m:
if custom:
return params, ''
else:
return {}, orig_s
params_update(params, 'group', 'cap_int3')
if 'atlas' not in s:
# If XxY --> x = X*11.08+0.37, y = Y*11.36+0.37
# If XpXXxYpYY --> x = X.XX, y = Y.YY
x = m.group('x')
if 'p' not in x:
x = int(x)*11.08+0.37
else:
x = float(x.replace('p', '.'))
x = round(x, 2)
y = m.group('y')
if 'p' not in y:
y = int(y)*11.36+0.37
else:
y = float(y.replace('p', '.'))
y = round(y, 2)
params_update(params, 'x', x)
params_update(params, 'y', y)
assert not m.group('v'), m.group('v')
assert not m.group('l'), m.group('l')
s = s[:m.start(0)]+s[m.end(0):]
s = s.strip('_')
if s in ('m1m4',):
s = ''
if 'met5' in s:
s = s.replace('met5', 'm5')
if s:
params_update(params, 'variant', s)
else:
t = m.group('type')
props = ['atlas']
if t == 'wafflecap':
props.append('waffle')
v = m.group('v')
if v is not None:
l = m.group('l')
assert l is None, l
params_update(params, 'l', int(v))
else:
assert custom, (v, custom)
elif t == 'fingercap':
props.append('finger')
v = m.group('v')
if v == '2':
params_update(params, 'x', 2.85)
elif v is None:
params_update(params, 'x', 2.7)
else:
assert False, (s, t, v)
l = m.group('l')
if l:
params_update(params, 'y', float(m.group('l'))+1.10)
elif t is None:
assert False, (s, t)
assert not m.group('x'), m.group('x')
assert not m.group('y'), m.group('y')
params_update(params, 'props', props)
if 'metal' not in params:
params_update(params, 'metal', ['UmetalU'])
if 'shield' not in params:
params_update(params, 'shield', ['UshieldU'])
return params, ''
elif m:
params_update(params, 'group', 'vppcap')
m = RE_CAP_PARAMS.search(s)
if not m:
if 'metal' not in params:
params_update(params, 'metal', [])
if 'shield' not in params:
params_update(params, 'shield', [])
props = []
if 'phv' in s:
props.append('phv')
s = s.replace('phv', '')
if 'nhvnative' in s:
props.append('nhv')
s = s.replace('nhvnative', '')
if '5x4' in s:
s = s.replace('5x4', '')
props.append('5x4')
if '10x4' in s:
s = s.replace('10x4', '')
props.append('10x4')
if '2x' in s:
s = s.replace('2x', '')
props.append('2x')
if 'noextrafingers' in s:
s = s.replace('noextrafingers', '')
props.append('noextrafingers')
params_update(params, 'props', props)
s = s.strip('_')
if s in ('nwell', 'subcell', 'raphael', 'top'):
params_update(params, 'variant', s)
s = ''
return params, s
s = s[:m.start(0)]+s[m.end(0):]
x = float(m.group('x').replace('p', '.'))
params_update(params, 'x', x)
y = float(m.group('y').replace('p', '.'))
params_update(params, 'y', y)
f = m.group('f')
if f is not None:
layers, x = get_layers(f)
assert not x, (f, layers, x)
assert custom, (f, layers, x, s, params)
#params_update(params, 'metal', layers)
elif 'metal' not in params:
params_update(params, 'metal', [])
variant = None
shield = m.group('shield')
if shield is not None:
layers, x = get_layers(shield)
if '0p4' in x:
x = x.replace('0p4', '')
if 'mod' in x:
x = x.replace('mod', '')
variant = 'm5mod'
assert x == 'shield', (shield, layers, x)
if 'broken name?' in params:
assert params['broken name?'], params
else:
assert len(layers) <= 2, (layers, shield, params)
params_update(params, 'shield', layers)
elif 'shield' not in params:
params_update(params, 'shield', [])
if m.group('v'):
variant = int(m.group('v'))
t = m.group('t')
if t and variant != 'm5mod':
assert variant is None, (t, orig_s, variant)
variant = t
if variant:
params_update(params, 'variant', variant)
return params, ''
##########################################################################
# Resistors
##########################################################################
RES_CUSTOM = {}
RES_EXTRA = set()
with open('res_custom.csv', newline='') as f:
for r in csv.DictReader(f):
if not r['key']:
continue
if r['key'][0] == '#':
continue
try:
r['layers'] = csv_parse_layers('layers', r['layers'])
if r['size']:
r['size'] = float(r['size'])
else:
del r['size']
if not r['broken name?']:
del r['broken name?']
else:
assert r['broken name?'] == 'TRUE', (r['broken name?'], r)
r['broken name?'] = True
if not r['props']:
r['props'] = []
else:
r['props'] = eval(r['props'])
if not r['example']:
del r['example']
k = r.pop('key')
RES_EXTRA.add(k)
RES_EXTRA.add(r.pop('src1'))
RES_EXTRA.add(r.pop('src2'))
assert k not in RES_CUSTOM, assert_pformat(k, r, RES_CUSTOM)
RES_CUSTOM[k] = dict(r)
except ValueError as e:
print('res_custom.csv', r, 'ValuError:', e)
raise
def parse_res(s):
"""
>>> p('par_polyres_sub')
({'basename': 'parasitics',
'device': 'special',
'esd': False,
'group': 'resistor',
'key': 'par_polyres_sub',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__parasitic__res_po'},
'')
('parasitics', 'sky130_fd_pr__model__parasitic__res_po')
>>> p('LIres')
({'device': 'resistor',
'esd': False,
'layers': ['li'],
'props': [],
'rf': False,
'type': 'generic'},
'')
('res_generic_l1', 'sky130_fd_pr__res_generic_l1')
>>> p('pDFres')
({'device': 'resistor',
'esd': False,
'layers': ['pdiff'],
'props': [],
'rf': False,
'type': 'generic'},
'')
('res_generic_pd', 'sky130_fd_pr__res_generic_pd')
>>> p('presistor')
({'basename': 'res_high_po',
'device': 'special',
'esd': False,
'group': 'resistor',
'key': 'presistor',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__res_high_po__base'},
'')
('res_high_po', 'sky130_fd_pr__res_high_po__base')
>>> p('xhrpoly_0p35')
({'device': 'resistor',
'esd': False,
'layers': ['poly'],
'props': [],
'rf': False,
'size': 0.35,
'type': 'high'},
'')
('res_high_po', 'sky130_fd_pr__res_high_po_0p35')
P diffusion Resistor
>>> p('mrdp')
({'device': 'resistor',
'esd': False,
'layers': ['pdiff'],
'props': [],
'rf': False,
'type': 'generic'},
'')
('res_generic_pd', 'sky130_fd_pr__res_generic_pd')
Isolated P-well resistor
>>> p('xpwres')
({'device': 'resistor',
'esd': False,
'layers': ['pwell'],
'props': ['iso'],
'rf': False,
'type': 'iso'},
'')
('res_iso_pw', 'sky130_fd_pr__res_iso_pw')
>>> p('mrp1')
({'device': 'resistor',
'esd': False,
'layers': ['poly'],
'props': [],
'rf': False,
'type': 'generic'},
'')
('res_generic_po', 'sky130_fd_pr__res_generic_po')
>>> p('mrl1')
({'device': 'resistor',
'esd': False,
'layers': ['li'],
'props': [],
'rf': False,
'type': 'generic'},
'')
('res_generic_l1', 'sky130_fd_pr__res_generic_l1')
>>> p('mrm1')
({'device': 'resistor',
'esd': False,
'layers': ['m1'],
'props': [],
'rf': False,
'type': 'generic'},
'')
('res_generic_m1', 'sky130_fd_pr__res_generic_m1')
>>> p('xuhrpoly_0p35')
({'device': 'resistor',
'esd': False,
'layers': ['poly'],
'props': ['high sheet'],
'rf': False,
'size': 0.35,
'type': 'xhigh'},
'')
('res_xhigh_po', 'sky130_fd_pr__res_xhigh_po_0p35')
>>> p('hrpoly_0p35_175320108')
({'device': 'resistor',
'esd': False,
'example': '175320108',
'layers': ['poly'],
'props': [],
'rf': False,
'size': 0.35,
'type': 'high'},
'')
('res_high_po', 'sky130_fd_pr__res_high_po_0p35__example1')
>>> p('hrpoly_0p35_l1m1con_175323180')
({'device': 'resistor',
'esd': False,
'example': '175323180',
'layers': ['poly', 'li', 'm1'],
'props': [],
'rf': False,
'size': 0.35,
'type': 'high'},
'')
('res_high_pol1m1', 'sky130_fd_pr__res_high_pol1m1_0p35__example1')
>>> p('xuhrpoly_2p85')
({'device': 'resistor',
'esd': False,
'layers': ['poly'],
'props': ['high sheet'],
'rf': False,
'size': 2.85,
'type': 'xhigh'},
'')
('res_xhigh_po', 'sky130_fd_pr__res_xhigh_po_2p85')
>>> p('HRPoly_5p73_RPL1con$$173905964')
({'device': 'resistor',
'esd': False,
'example': '173905964',
'layers': ['poly', 'li'],
'props': [],
'rf': False,
'size': 5.73,
'type': 'high'},
'')
('res_high_pol1', 'sky130_fd_pr__res_high_pol1_5p73__example1')
>>> p('hrpoly_0p35$$175320108')
({'device': 'resistor',
'esd': False,
'example': '175320108',
'layers': ['poly'],
'props': [],
'rf': False,
'size': 0.35,
'type': 'high'},
'')
('res_high_po', 'sky130_fd_pr__res_high_po_0p35__example1')
>>> p('hrpoly_5p73_l1m1con_173908012')
({'device': 'resistor',
'esd': False,
'example': '173908012',
'layers': ['poly', 'li', 'm1'],
'props': [],
'rf': False,
'size': 5.73,
'type': 'high'},
'')
('res_high_pol1m1', 'sky130_fd_pr__res_high_pol1m1_5p73__example1')
"""
orig_s = s
params = {}
if s in RES_CUSTOM:
params_update_multi(params, RES_CUSTOM[s])
s, cparams = parse_common(s)
params_update_multi(params, cparams)
if 'par' in s:
s = s.replace('par', '_')
params_update(params, 'parasitic', True)
s = s.strip('_')
if s.startswith('xuhr') or s.startswith('xhr'):
s = s[1:]
props = []
if s in ('xpwres', 'isopwellres'):
params_update(params, 'device', 'resistor')
params_update(params, 'type', 'iso')
params_update(params, 'layers', ['pwell'])
props.append('iso')
s = ''
elif s.startswith('mr'):
layers, x = get_layers(s[2:])
assert layers, (layers, x, s)
assert not x, (layers, x, s)
params_update(params, 'device', 'resistor')
params_update(params, 'type', 'generic')
params_update(params, 'layers', layers)
s = ''
elif s.startswith('hrpoly'):
params_update(params, 'device', 'resistor')
params_update(params, 'type', 'high')
s = s[len('hrpoly'):].strip('_')
layers = ['poly']
if 'con' in s:
con = [x for x in s.split('_') if x.endswith('con')]
assert len(con) == 1, (s, con)
assert con[0].endswith('con'), (s,)
s = s.replace(con[0], '')
con = con[0][:-len('con')]
layers, x = get_layers(con)
if 'poly' not in layers:
layers.insert(0, 'poly')
assert layers, (layers, x, con, s)
assert not x, (layers, x, con, s)
params_update(params, 'layers', layers)
elif s.startswith('uhrpoly'):
params_update(params, 'device', 'resistor')
params_update(params, 'layers', ['poly'])
params_update(params, 'type', 'xhigh')
props.append('high sheet')
s = s[len('xuhrpoly'):].strip('_')
elif 'res' in s or s.startswith('xr'):
assert s in RES_CUSTOM, (s, params)
return params, ''
else:
return None, orig_s
params_update(params, 'props', props)
s = s.strip('_')
if 'p' in s:
p = [x for x in s.split('_') if 'p' in x]
assert len(p) == 1, (s, p)
assert 'p' in p[0], (s, p)
p = p[0]
s = s.replace(p, '')
params_update(params, 'size', float(p.replace('p', '.')))
s = s.strip('_')
if s:
params_update(params, 'example', s)
s = ''
return params, s
##########################################################################
# Diode
##########################################################################
DIODE_CUSTOM = {}
def cleanup_diode(s):
"""
>>> cleanup_diode('ndiode')
'n_diode'
>>> cleanup_diode('dnwdiode_psub_n20zvtvh1defet')
'dnw_diode_psub_n20zvtvhdefet'
>>> cleanup_diode('dnwdiode_p20vhv1')
'dnw_diode_p20vhv'
>>> cleanup_diode('dnwdiode_psub_n20nativevhv1')
'dnw_diode_psub_n20nativevhv'
>>> cleanup_diode('condiode')
'con_diode'
>>> cleanup_diode('condiodeHvPsub')
'con_diode_hvpsub'
>>> cleanup_diode('xesd_dnwdiode_pw_100')
'rf_esd_dnw_diode_pw_100'
>>> cleanup_diode('xesd_ndiode_h_100')
'rf_esd_n_diode_h_100'
"""
s = s.lower()
# vhv1 --> vhv
# vh1 --> vh
s = re.sub('((?:vhv)|(?:vh))1', '\\1', s)
# Make xxxdiodexxx --> xxx_diode_xxx
s = s.replace('condiode', 'con_diode')
s = re.sub('([^_])diode', '\\1_diode', s)
s = re.sub('diode([^_])', 'diode_\\1', s)
s = s.replace('xesd_', 'rf_esd_')
s = re.sub('_+', '_', s)
if s.startswith('_'):
s = s[1:]
if s.endswith('_'):
s = s[:-1]
return s
def parse_diode(s):
"""
>>> p('dnwhvdiode_psub')
({'corners': ['hv'],
'device': 'pardiode',
'esd': False,
'for': 'generic',
'parasitic': True,
'props': [],
'rf': False,
'type': 'deep nwell diode -|<- psub',
'vcc': '16v0',
'vrange': 'very'},
'')
('parasitics', 'sky130_fd_pr__model__parasitic__diode_ps2dn__hv')
>>> p('xesd_ndiode_h_dnwl_100')
({'corners': [],
'device': 'pardiode',
'esd': True,
'for': {'corners': [],
'device': 'diode',
'esd': True,
'parasitic': False,
'props': [],
'rf': True,
'type': 'n diode',
'variant': '100',
'vcc': '11v0',
'vrange': 'high'},
'parasitic': True,
'props': [],
'rf': True,
'type': 'deep nwell diode -|<- '
'pwell'},
'')
('esd_rf_diode_pw2nd_11v0', 'sky130_fd_pr__esd_rf_diode_pw2nd_11v0_100__parasitic__diode_pw2dn')
>>> p('xesd_ndiode_h_100')
({'corners': [],
'device': 'diode',
'esd': True,
'parasitic': False,
'props': [],
'rf': True,
'type': 'n diode',
'variant': '100',
'vcc': '11v0',
'vrange': 'high'},
'')
('esd_rf_diode_pw2nd_11v0', 'sky130_fd_pr__esd_rf_diode_pw2nd_11v0_100')
>>> p('nwdiode')
({'corners': [],
'device': 'pardiode',
'esd': False,
'for': 'generic',
'parasitic': True,
'props': [],
'rf': False,
'type': 'nwell diode',
'vcc': '16v0',
'vrange': 'very'},
'')
('parasitics', 'sky130_fd_pr__model__parasitic__diode_ps2nw')
>>> p('xndiode')
({'corners': [],
'device': 'diode',
'esd': True,
'parasitic': False,
'props': [],
'rf': False,
'type': 'n diode',
'vcc': '5v5',
'vrange': 'high'},
'')
('esd_diode_pw2nd_05v5', 'sky130_fd_pr__esd_diode_pw2nd_05v5')
>>> p('ndiode_lvt')
({'corners': [],
'device': 'diode',
'esd': False,
'parasitic': False,
'props': [],
'rf': False,
'type': 'n diode',
'vcc': '5v5',
'vrange': 'high',
'vt': 'low'},
'')
('diode_pw2nd_05v5_lvt', 'sky130_fd_pr__diode_pw2nd_05v5_lvt')
>>> p('pdiode_hvt')
({'corners': [],
'device': 'diode',
'esd': False,
'parasitic': False,
'props': [],
'rf': False,
'type': 'p diode',
'vcc': '5v5',
'vrange': 'high',
'vt': 'high'},
'')
('diode_pd2nw_05v5_hvt', 'sky130_fd_pr__diode_pd2nw_05v5_hvt')
Pwell-Deep Nwell Diode
RF ESD PW-DNW Diode
Pwell-Deep Nwell Diode 16 dnwdiode_pw
RF ESD PW-DNW Diode 16 xesd_dnwdiode_pw_X
RF Pwell-Deep Nwell Diode 11 xdnwdiode_pwell_rf
Psub-Deep Nwell Diode 16 dnwdiode_psub
MODELS/SPECTRE/s8x/Models/dnwdiode_pw.mod
- dnwdiode_pw
- dnwdiode_pw_no_rs
- xesd_ndiode_h_dnwl_300
- xdnwdiode_pwell_rf
>>> p('nwdiode_no_rs')
({'basename': 'parasitics',
'device': 'special',
'esd': False,
'group': 'model',
'key': 'nwdiode_no_rs',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__parasitic__diode_ps2nw_noresistor'},
'')
('parasitics', 'sky130_fd_pr__model__parasitic__diode_ps2nw_noresistor')
>>> p('dnwdiode_pw')
({'corners': [],
'device': 'pardiode',
'esd': False,
'for': 'generic',
'parasitic': True,
'props': [],
'rf': False,
'type': 'deep nwell diode -|<- pwell',
'vcc': '16v0',
'vrange': 'very'},
'')
('parasitics', 'sky130_fd_pr__model__parasitic__diode_pw2dn')
>>> p('xdnwdiode_pwell_rf')
({'corners': [],
'device': 'pardiode',
'esd': True,
'for': 'generic',
'parasitic': True,
'props': [],
'rf': True,
'type': 'deep nwell diode -|<- pwell',
'vcc': '11v0',
'vrange': 'high'},
'')
('parasitics', 'sky130_fd_pr__model__parasitic__rf_diode_pw2dn')
MODELS/SPECTRE/s8x/Models/dnwdiode_p20vhv1.mod
- p20vhv_vb = 31.0
>>> p('dnwdiode_p20vhv1')
({'corners': [],
'device': 'pardiode',
'esd': False,
'for': {'device': 'fet',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'pfet',
'vcc': '20v0',
'vrange': 'ultra'},
'parasitic': True,
'props': [],
'rf': False,
'type': 'deep nwell diode -|<- '
'pwell'},
'')
('pfet_20v0', 'sky130_fd_pr__pfet_20v0__parasitic__diode_pw2dn')
MODELS/SPECTRE/s8x/Models/dnwdiode_psub_n20nativevhv1.mod
- n20vhv1_vb = 40.0
>>> p('dnwdiode_psub_n20nativevhv1')
({'corners': [],
'device': 'pardiode',
'esd': False,
'for': {'device': 'fet',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'nfet',
'vcc': '20v0',
'vrange': 'ultra',
'vt': 'native'},
'parasitic': True,
'props': [],
'rf': False,
'type': 'deep nwell diode -|<- psub'},
'')
('nfet_20v0_nvt', 'sky130_fd_pr__nfet_20v0_nvt__parasitic__diode_ps2dn')
MODELS/SPECTRE/s8x/Models/dnwdiode_psub_n20zvtvh1defet.mod
- n20zvtvhv1_vb = 30.0
>>> p('dnwdiode_psub_n20zvtvh1defet')
({'corners': ['extended_drain'],
'device': 'pardiode',
'esd': False,
'for': {'device': 'fet',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'nfet',
'vcc': '20v0',
'vrange': 'ultra',
'vt': 'zero'},
'parasitic': True,
'props': [],
'rf': False,
'type': 'deep nwell diode -|<- psub'},
'')
('nfet_20v0_zvt', 'sky130_fd_pr__nfet_20v0_zvt__parasitic__diode_ps2dn__extended_drain')
>>> p('dnwdiode_pvhv')
({'corners': [],
'device': 'pardiode',
'esd': False,
'for': {'device': 'fet',
'esd': False,
'props': [],
'rf': False,
'subdevice': 'pfet',
'vcc': 'g5v0d16v0',
'vrange': 'very'},
'parasitic': True,
'props': [],
'rf': False,
'type': 'deep nwell diode -|<- '
'pwell'},
'')
('pfet_g5v0d16v0', 'sky130_fd_pr__pfet_g5v0d16v0__parasitic__diode_pw2dn')
>>> p('dnwdiode_pw_defet')
({'corners': ['extended_drain'],
'device': 'pardiode',
'esd': False,
'for': 'generic',
'parasitic': True,
'props': [],
'rf': False,
'type': 'deep nwell diode -|<- pwell',
'vcc': '16v0',
'vrange': 'very'},
'')
('parasitics', 'sky130_fd_pr__model__parasitic__diode_pw2dn__extended_drain')
>>> p('ndiode_defet')
({'corners': ['extended_drain'],
'device': 'diode',
'esd': False,
'parasitic': False,
'props': [],
'rf': False,
'type': 'n diode',
'vcc': '5v5',
'vrange': 'high'},
'')
('diode_pw2nd_05v5', 'sky130_fd_pr__diode_pw2nd_05v5__extended_drain')
>>> p('condiode')
({'basename': '',
'device': 'special',
'esd': False,
'group': 'model',
'key': 'condiode',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__diode_connection'},
'')
('', 'sky130_fd_pr__model__diode_connection')
>>> p('condiodeHvPsub')
({'basename': 'parasitics',
'device': 'special',
'esd': False,
'group': 'diode',
'key': 'condiodehvpsub',
'props': [],
'rf': False,
'typename': 'sky130_fd_pr__model__parasitic__diode_ps2dn_highvoltage'},
'')
('parasitics', 'sky130_fd_pr__model__parasitic__diode_ps2dn_highvoltage')
>>> d = parse_name('dnwdiode_psub')
>>> newname(d[0])
('parasitics', 'sky130_fd_pr__model__parasitic__diode_ps2dn')
"""
if 'diode' not in s:
return None, s
orig_s = s
params = {}
if s in DIODE_CUSTOM:
params_update_multi(params, DIODE_CUSTOM[s])
s = cleanup_diode(s)
s, cparams = parse_common(s)
if s.startswith('x'):
cparams['esd'] = True
s = s[1:]
params_update_multi(params, cparams)
#######
dtype = ""
if 'dnwhv_diode' in s:
params_update(params, 'corners', ['hv'])
s = s.replace('dnwhv_diode', 'dnw_diode')
# xesd_n_diode_h_dnwl_X RF ESD HV Deep Nwell nDiode 16.0v -- Special case!
if s.startswith('n_diode_h_dnwl'):
params_update(params, 'device', 'pardiode')
params_update(params, 'parasitic', True)
params_update(params, 'esd', True)
params_update(params, 'rf', True)
base_diode_str = 'xesd_'+s.replace('_dnwl', '').replace('_diode', 'diode')
base_diode, s = parse_diode(base_diode_str)
assert not s, s
assert not base_diode['parasitic'], assert_pformat('not', base_diode['parasitic'], base_diode)
assert base_diode['esd'], assert_pformat('', base_diode['esd'], base_diode)
assert base_diode['rf'], assert_pformat('', base_diode['rf'], base_diode)
assert 'diode' == base_diode['device'], assert_pformat('diode', base_diode['device'], base_diode)
params_update(params, 'for', base_diode)
params_update(params, 'type', 'deep nwell diode -|<- pwell')
return params, s
# Name Diode Description VCC
# n_diode nDiode 5.5v
# n_diode_lvt Low Vt nDiode 5.5v
# n_diode_native Native nDiode 5.5v
# xn_diode ESD N+/Pwell diode 5.5v
# n_diode_h HV nDiode 11.0v
# xn_diode_h ESD HV N+/Pwell diode 11.0v
# xesd_n_diode_h_X RF ESD HV nDiode 11.0v
elif s.startswith('n_diode'):
dtype = "n diode"
s = s[len('n_diode'):]
params_update(params, 'parasitic', False)
# Name Diode Description VCC
# p_diode pDiode 5.5v
# p_diode_hvt High Vt pDiode 5.5v
# p_diode_lvt Low Vt pDiode 5.5v
# xp_diode ESD P+/Nwell diode 5.5v
# xp_diode_h ESD HV P+/Nwell diode 11.0v
# p_diode_h HV pDiode 11.0v
# xesd_p_diode_h_X RF ESD HV pDiode 11.0v
elif s.startswith('p_diode'):
dtype = "p diode"
s = s[len('p_diode'):]
params_update(params, 'parasitic', False)
# Name Diode Description VCC
# dnw_diode_psub (Parasitic) Psub-Deep Nwell Diode 16.0v
# dnw_diode_psub (Parasitic) Photo Diode 16.0v
# dnw_diode_pw (Parasitic) Pwell-Deep Nwell Diode 16.0v
# xesd_dnw_diode_pw_X (Parasitic) RF ESD PW-DNW Diode 16.0v
# xdnw_diode_pwell_rf (Parasitic) RF Pwell-Deep Nwell Diode 11.0v
elif s.startswith('dnw_diode'):
dtype = "deep nwell diode"
s = s[len('dnw_diode'):]
params_update(params, 'parasitic', True)
# Name Diode Description VCC
# nw_diode (Parasitic) Nwell Diode 16.0v
# xnw_diode_rf (Parasitic) RF Nwell Diode 11.0v
elif s.startswith('nw_diode'):
dtype = "nwell diode"
s = s[len('nw_diode'):]
params_update(params, 'parasitic', True)
else:
assert not params, (orig_s, s, params)
return None, orig_s
if not params['parasitic']:
params_update(params, 'device', 'diode')
# pdiode_hvt
vt_check = {
'lvt' : 'low',
'hvt' : 'high',
'zvt' : 'zero',
'native': 'native',
}
for v in vt_check:
if v in s:
s = s.replace(v, '')
params_update(params, 'vt', vt_check[v])
if '_h' in s:
params_update(params, 'vcc', '11v0')
params_update(params, 'vrange', 'high')
s = s.replace('_h', '')
elif 'vhv' in s:
params_update(params, 'vrange', 'ultra')
params_update(params, 'vcc', '20v0')
s = s.replace('vhv', '')
else:
params_update(params, 'vcc', '5v5')
params_update(params, 'vrange', 'high')
s = s.strip('_')
if s:
params_update(params, 'variant', s)
s = ''
else:
assert params['parasitic'], (orig_s, s, params)
params_update(params, 'device', 'pardiode')
if '_psub' in s:
dtype += ' -|<- psub'
s = s.replace('_psub', '')
elif '_pwell' in s:
dtype += ' -|<- pwell'
s = s.replace('_pwell', '')
elif '_pw' in s:
dtype += ' -|<- pwell'
s = s.replace('_pw', '')
# For parasitic diodes, they can be for a specific FET model
# dnwdiode_psub_n20zvtvh1defet
fet = None
if s:
fet, fs = parse_fet(s)
if fet:
params_update(params, 'for', fet)
if dtype == 'deep nwell diode':
assert fet['subdevice'] == 'pfet', fet
dtype += ' -|<- pwell'
s = fs
else:
params_update(params, 'for', 'generic')
if params['rf']:
params_update(params, 'vcc', '11v0')
params_update(params, 'vrange', 'high')
else:
params_update(params, 'vcc', '16v0')
params_update(params, 'vrange', 'very')
params_update(params, 'type', dtype)
if 'corners' not in params:
params['corners'] = []
if 'props' not in params:
params['props'] = []
return params, s
##########################################################################
# SRAM / Flash
##########################################################################
# Unable to decode ,sonos_bol,cor
# Unable to decode ,sonos_bol_e_see,pm3
# Unable to decode ,sonos_bol_p_see,pm3
# Unable to decode ,sonos_eol_bol,scs
# Unable to decode ,sonos_ffeol,cor
# Unable to decode ,sonos_ffteol,cor
# Unable to decode ,sonos_see_bol,cor
# Unable to decode ,sonos_see_bol_e,cor
# Unable to decode ,sonos_see_bol_p,cor
# Unable to decode ,sonos_see_eol,cor
# Unable to decode ,sonos_see_eol_e,cor
# Unable to decode ,sonos_see_eol_p,cor
# Unable to decode ,sonos_see_ffeol,cor
# Unable to decode ,sonos_see_ffeol_e,cor
# Unable to decode ,sonos_see_ffeol_p,cor
# Unable to decode ,sonos_see_ffteol,cor
# Unable to decode ,sonos_see_ffteol_e,cor
# Unable to decode ,sonos_see_ffteol_p,cor
# Unable to decode ,sonos_see_tbol,cor
# Unable to decode ,sonos_see_tbol_e,cor
# Unable to decode ,sonos_see_tbol_p,cor
# Unable to decode ,sonos_see_teol,cor
# Unable to decode ,sonos_see_teol_e,cor
# Unable to decode ,sonos_see_teol_p,cor
# Unable to decode ,sonos_see_wbol,cor
# Unable to decode ,sonos_see_wbol_e,cor
# Unable to decode ,sonos_see_wbol_p,cor
# Unable to decode ,sonos_sseol,cor
# Unable to decode ,sonos_ssteol,cor
# Unable to decode ,sonos_tbol,cor
# Unable to decode ,sonos_tteol,cor
# Unable to decode ,sonos_ttteol,cor
# Unable to decode ,sonos_wbol,cor
SPECIAL_CUSTOM = {}
with open('spe_custom.csv', newline='') as f:
for r in csv.DictReader(f):
if not r['key']:
continue
if r['key'][0] == '#':
continue
try:
if not r['props']:
r['props'] = []
else:
r['props'] = eval(r['props'])
for k in list(r.keys()):
if k == 'basename':
continue
if r[k] == '':
del r[k]
k = r['key']
assert k not in SPECIAL_CUSTOM, assert_pformat(k, r, SPECIAL_CUSTOM)
SPECIAL_CUSTOM[k] = dict(r)
except ValueError as e:
print('spe_custom.csv', r, 'ValuError:', e)
raise
RE_NUMBER = re.compile("_([0-9]+)$")
def parse_special(s):
"""
>>> p('npassd')
({'basename': 'special_nfet_pass_dual',
'device': 'special',
'esd': False,
'group': 'sram',
'key': 'npassd',
'props': [],
'rf': False,
'subgroup': 'dual',
'subtype': 'pass',
'type': 'n',
'typename': 'sky130_fd_pr__special_nfet_pass_dual'},
'')
('special_nfet_pass_dual', 'sky130_fd_pr__special_nfet_pass_dual')
>>> p('ppu')
({'basename': 'special_pfet_pass',
'device': 'special',
'esd': False,
'group': 'sram',
'key': 'ppu',
'props': [],
'rf': False,
'subgroup': 'single',
'subtype': 'latch',
'type': 'p',
'typename': 'sky130_fd_pr__special_pfet_pass'},
'')
('special_pfet_pass', 'sky130_fd_pr__special_pfet_pass')
>>> p('nlvtpass_ff_discrete')
({'basename': 'special_nfet_pass_lvt',
'corners': ['ff', 'discrete'],
'device': 'special',
'esd': False,
'group': 'sram',
'key': 'nlvtpass',
'props': [],
'rf': False,
'subgroup': 'single',
'subtype': 'pass',
'type': 'n',
'typename': 'sky130_fd_pr__special_nfet_pass_lvt',
'vt': 'low'},
'')
('special_nfet_pass_lvt', 'sky130_fd_pr__special_nfet_pass_lvt__ff_discrete')
"""
orig_s = s
params = {}
if s.endswith('1') and not s.endswith('x1'):
s = s[:1]
m = RE_NUMBER.search(s)
example = None
if m:
s = s[:m.start(0)]
example = m.group(1)
if s in SPECIAL_CUSTOM:
params_update_multi(params, SPECIAL_CUSTOM[s])
s, cparams = parse_common(s)
params_update_multi(params, cparams)
if s in SPECIAL_CUSTOM:
params_update_multi(params, SPECIAL_CUSTOM[s])
if 'basename' not in params:
return None, orig_s
if example:
params_update(params, 'example', example)
params_update(params, 'device', 'special')
return params, ''
# FIXME: ???
# Unable to decode s8phirs_10r,DFL1
# Unable to decode s8phirs_10r,DFL1sd
# Unable to decode s8phirs_10r,DFL1sd2
# Unable to decode s8phirs_10r,DFL1sdf
# Unable to decode s8phirs_10r,DFL1sq
# Unable to decode s8phirs_10r,DFM1
# Unable to decode s8phirs_10r,DFM1sd
# Unable to decode s8phirs_10r,DFM1sd2
# Unable to decode s8phirs_10r,DFTPL1s
# Unable to decode s8phirs_10r,DFTPL1s2
# Unable to decode s8phirs_10r,DFTPL1sw
# Unable to decode s8phirs_10r,DFTPM1s
# Unable to decode s8phirs_10r,DFTPM1s2
# Unable to decode s8phirs_10r,DFTPM1s2enh
# Unable to decode s8phirs_10r,DFTPM1sw
# Unable to decode s8phirs_10r,hvDFL1sd
# Unable to decode s8phirs_10r,hvDFL1sd2
# Unable to decode s8phirs_10r,hvDFM1sd
# Unable to decode s8phirs_10r,hvDFM1sd2
# Unable to decode s8phirs_10r,hvDFTPL1s
# Unable to decode s8phirs_10r,hvDFTPL1s2
# Unable to decode s8phirs_10r,hvDFTPM1s
# Unable to decode s8phirs_10r,hvDFTPM1s2
# Unable to decode s8phirs_10r,hvDFTPM1s2enh
##########################################################################
# Vias
##########################################################################
VIA_CUSTOM = {}
with open('via_custom.csv', newline='') as f:
for r in csv.DictReader(f):
if not r['key']:
continue
if r['key'][0] == '#':
continue
try:
if not r['props']:
r['props'] = []
else:
r['props'] = eval(r['props'])
r['layers'] = csv_parse_layers('layers', r['layers'])
for k in list(r.keys()):
if r[k] == '':
del r[k]
k = r.pop('key')
assert k not in VIA_CUSTOM, assert_pformat(k, r, VIA_CUSTOM)
VIA_CUSTOM[k] = dict(r)
except ValueError as e:
print('via_custom.csv', r, 'ValuError:', e)
raise
RE_VIA = re.compile('(?P<layers>([plm][y1-5])([lmr][1-5d]l?))(?P<corner>(_[crt])?(?P<variant>sq)?)')
def parse_via(s):
"""
>>> p('pyl1')
({'device': 'via',
'layers': ['poly', 'li']},
'')
('via_pol1', 'sky130_fd_pr__via_pol1')
>>> p('PYM1$$179588140')
({'device': 'via',
'example': '179588140',
'layers': ['poly', 'm1'],
'props': []},
'')
('via_pom1', 'sky130_fd_pr__via_pom1__example1')
>>> p('PYM1')
({'device': 'via',
'layers': ['poly', 'm1'],
'props': []},
'')
('via_pom1', 'sky130_fd_pr__via_pom1')
>>> p('l1m1')
({'device': 'via',
'layers': ['li', 'm1'],
'props': []},
'')
('via_l1m1', 'sky130_fd_pr__via_l1m1')
>>> p('m5rdl')
({'device': 'via',
'layers': ['m5', 'rdl'],
'props': []},
'')
('via_m5r1', 'sky130_fd_pr__via_m5r1')
Okay, the names like `dfl1sd` are names that Virtuoso gives to contacts,
where the pcell generator creates tiny subcells out of individual
contacts. "DF" is diffusion, "hvDF" is high-voltage diffusion, "SD" is
source/drain, and "L1" means connects to local interconnect. The "2" at
the end probably means the contact has two cuts (i.e., double-width
contact).
>>> p('hvDFL1sd2')
"""
orig_s = s
params = {}
if s in VIA_CUSTOM:
params_update_multi(params, VIA_CUSTOM[s])
params_update(params, 'device', 'via')
m = RE_VIA.match(s)
if not m:
return None, orig_s
layers, s = get_layers(m.group('layers'))
assert len(layers) == 2, (layers, m.group('layers'))
params_update(params, 'layers', layers)
variant = m.group('variant')
if variant:
params_update(params, 'variant', variant)
corner = m.group('corner')
if corner:
return None, orig_s
params_update(params, 'corners', corner)
return params, s
##########################################################################
# Indictors
##########################################################################
def parse_ind(s):
"""
>>> p('xind_5_220')
({'device': 'inductor',
'esd': False,
'rf': False,
'variant': '220',
'y': 5},
'')
('ind_05', 'sky130_fd_pr__ind_05_220')
>>> p('xind4_01rf')
({'device': 'inductor',
'esd': False,
'rf': True,
'x': 4,
'y': 1},
'')
('ind_01_04', 'sky130_fd_pr__ind_01_04')
>>> p('xinductor')
({'device': 'inductor',
'esd': False,
'rf': False},
'')
('ind', 'sky130_fd_pr__ind')
>>> p('ind_03')
({'device': 'inductor',
'esd': False,
'float': [],
'group': 'inductor',
'metal': [],
'props': [],
'rf': False,
'shield': [],
'y': 3.0},
'')
('ind_03', 'sky130_fd_pr__ind_03')
>>> p('rf_xind4_01')
({'device': 'inductor',
'esd': False,
'float': [],
'group': 'inductor',
'metal': [],
'props': [],
'rf': True,
'shield': [],
'x': 4.0,
'y': 1.0},
'')
('ind_01_04', 'sky130_fd_pr__ind_01_04')
>>> p('s8blref_xind4_01')
({'broken name?': True,
'device': 'inductor',
'esd': False,
'float': [],
'group': 'inductor',
'metal': [],
'props': [],
'rf': False,
'shield': [],
'x': 4.0,
'y': 1.0},
'')
('ind_01_04', 'sky130_fd_pr__ind_01_04')
"""
if "ind" not in s:
return None, s
orig_s = s
params = {}
if s in CAP_CUSTOM:
params_update_multi(params, CAP_CUSTOM[s])
# esd / rf / corners
s, cparams = parse_common(s)
params_update_multi(params, cparams)
params_update(params, 'device', 'inductor')
bits = list(s.split('_'))
if bits and bits[0] == 's8blref':
bits.pop(0)
if bits:
b0 = bits.pop(0)
assert b0 in ('xinductor', 'xind', 'ind', 'xind4', 'ind4'), (b0, bits, s)
if b0.endswith('4'):
params_update(params, 'x', 4)
if bits:
b1 = bits.pop(0)
v = int(b1.strip('0'))
params_update(params, 'y', v)
if bits:
b2 = bits.pop(0)
v = int(b2.strip('0'))
params_update(params, 'variant', b2)
assert not bits, (bits, b0, b1, b2, s)
return params, ''
##########################################################################
# Generic
##########################################################################
def _parse_name_ignore(s):
if not s.strip():
return True
if 'scs8' in s:
return True
if s.startswith('iso') and not s.startswith('isopwellres'):
return True
if 'probe' in s:
return True
if 'fill_diode_' in s:
return True
if s.startswith('diode_'):
b = s.split('_')
if len(b) == 2 and b[1] in [str(x) for x in range(0,32)]:
return True
if "proxy" in s:
return True
if "_backup" in s:
return True
if "_old" in s:
return True
return False
def _parse_name(s):
a, b = parse_special(s)
if a:
return a, b
if 'xc' in s:
return parse_cap(s)
if 'vpp' in s:
return parse_cap(s)
if 'mim' in s:
return parse_cap(s)
if 'diode' in s:
return parse_diode(s)
if 'npn' in s:
return parse_bjt(s)
if 'pnp' in s:
return parse_bjt(s)
if 'ind' in s:
return parse_ind(s)
if s.startswith('xr'):
return parse_res(s)
if 'mr' in s:
return parse_res(s)
if 'hr' in s:
return parse_res(s)
if 'res' in s and 'stress' not in s:
return parse_res(s)
if len(s) in (4, 5):
a, b = parse_via(s)
if a:
return a, b
a, b = parse_bjt(s)
if a:
return a, b
try:
a, b = parse_fet(s)
if a:
return a, b
except Exception as e:
print(s, 'Exception:', e)
return {}, s
def parse_name(s):
s = fix_duplicate(s)
orig_s = s
s = s.lower()
if _parse_name_ignore(s):
return {}, orig_s
example = None
if '$$' in s:
s, example = s.split('$$')
d, extra = _parse_name(s)
if not d:
return {}, orig_s
if example:
assert not 'example' in d, d
d['example'] = example
return d, extra
# This needs to happen after parse_name is run...
with open('dio_custom.csv', newline='') as f:
for r in csv.DictReader(f):
if not r['key']:
continue
if r['key'][0] == '#':
continue
try:
if not r['props']:
r['props'] = []
else:
r['props'] = eval(r['props'])
r['parasitic'] = csv_parse_bool(r['parasitic'])
r['esd'] = csv_parse_bool(r['esd'])
r['rf'] = csv_parse_bool(r['rf'])
if not r['vt']:
del r['vt']
if not r['vcc']:
del r['vcc']
if not r['variant']:
del r['variant']
if not r['for']:
del r['for']
elif r['for'] != 'generic':
r['for'] = parse_name(r['for'])[0]
r['corners'] = eval(r['corners'])
k = r.pop('key')
assert k not in DIODE_CUSTOM, assert_pformat(k, r, DIODE_CUSTOM)
DIODE_CUSTOM[k] = dict(r)
except ValueError as e:
print(r, 'ValuError:', e)
raise
raise
#################################################################
#################################################################
#################################################################
# -----
# -----
NEWNAME_LAYERS_WIDTH=2
NEWNAME_LAYERS = {
'deep nwell': ('dn','dnw','dnwl',), # Deep-Nwell
'psub' : ('ps','psb','psub',), # P Substrate
'pwell' : ('pw','pwl','pwll',), # Pwell
'nwell' : ('nw','nwl','nwll',), # Nwell
'ndiff' : ('nd','ndf','ndif',), # N Diffusion
'pdiff' : ('pd','pdf','pdif',), # P Diffusion
'poly' : ('po','ply','poly',), # Poly
'li' : ('l1','lin','lin1',), # Local-interconnect
'm1' : ('m1','mt1','met1',), # Metal 1
'm2' : ('m2','mt2','met2',), # Metal 1
'm3' : ('m3','mt3','met3',), # Metal 1
'm4' : ('m4','mt4','met4',), # Metal 1
'm5' : ('m5','mt5','met5',), # Metal 1
'rdl' : ('r1','rd1','rdl1',), # Redistribution Layer 1
'UmetalU' : ('UmetalU','UmetalU','UmetalU',),
'UshieldU' : ('UshieldU','UshieldU','UshieldU',),
}
for k in NEWNAME_LAYERS:
NEWNAME_LAYERS[k] = NEWNAME_LAYERS[k][NEWNAME_LAYERS_WIDTH-2]
assert (
len(set(NEWNAME_LAYERS.keys())) ==
len(set(NEWNAME_LAYERS.values()))
), "Duplicate new layer name!"
NEWNAME_LAYERS['x'] = 'UmetalU'
NEWNAME_VT = {
'low' : 'lvt',
'med' : 'mvt',
'high' : 'hvt',
'zero' : 'zvt',
'native' : 'nvt',
}
def _newname_sf(f):
"""
>>> _newname_sf(0.2)
'0p20'
>>> _newname_sf(2)
'2p00'
>>> _newname_sf(2.02)
'2p02'
>>> _newname_sf(1.77)
'1p77'
>>> _newname_sf(4.38)
'4p38'
>>> _newname_sf(4.59)
'4p59'
>>> _newname_sf(4.55)
'4p55'
>>> _newname_sf(4.49)
'4p49'
>>> _newname_sf(1.45)
'1p45'
>>> _newname_sf(1.73)
'1p73'
>>> _newname_sf(1.69)
'1p69'
"""
if not isinstance(f, float):
f = float(f)
f = round_half_up(f, 2)
return ('%04.2f' % f).replace('.', 'p')
def _newname_f(f):
"""
>>> _newname_f(10.2)
'10p2'
>>> _newname_f(2)
'02p0'
>>> _newname_f(2.02)
'02p0'
>>> _newname_f(1.77)
'01p8'
>>> _newname_f(4.38)
'04p4'
>>> _newname_f(4.59)
'04p6'
>>> _newname_f(4.55)
'04p6'
>>> _newname_f(4.49)
'04p5'
>>> _newname_f(11.45)
'11p5'
>>> _newname_f(11.73)
'11p7'
>>> _newname_f(11.69)
'11p7'
"""
if not isinstance(f, float):
f = float(f)
f = round_half_up(f, 1)
return ('%04.1f' % f).replace('.', 'p')
def _newname_i(i, w=2):
"""
>>> _newname_i(10)
'10'
>>> _newname_i(1)
'01'
"""
assert not isinstance(i, float), repr(i)
if not isinstance(i, int):
i = int(i)
assert w == 2, w
return '%02d' % i
def _newname_size(d):
"""
>>> _newname_size({'x': 10.2, 'y': 1.01})
'10p2x01p0'
>>> _newname_size({'x': 2, 'y': 10.05})
'02p0x10p1'
"""
x = d.get('x', None)
assert x is not None, (x, d)
y = d.get('y', None)
assert y is not None, (y, d)
return _newname_f(x)+'x'+_newname_f(y)
raise ValueError('No size in '+repr(d))
def _newname_wl(d):
"""
>>> _newname_wl({'width': 0.22, 'length': 1.15})
'W0p22L1p15'
>>> _newname_wl({'width': 0.22})
'W0p22'
"""
s = ''
width = d.get('width', None)
assert width is not None, (width, d)
s += 'W'+_newname_sf(width)
length = d.get('length', None)
if length is None:
s += '' #'XpXX'
else:
s += 'L'+_newname_sf(length)
return s
def _newname_vcc(vcc):
"""
>>> _newname_vcc('5.0V')
'05v0'
>>> _newname_vcc('5v5')
'05v5'
>>> _newname_vcc('5v')
'05v0'
>>> _newname_vcc('16v')
'16v0'
>>> _newname_vcc('-5v')
'n5v0'
"""
if '.' in vcc:
vcc = vcc.lower().replace('v', '')
vcc = vcc.replace('.', 'v')
assert 'v' in vcc
if vcc[0] == '-':
vcc = 'n'+vcc[1:]
if vcc[1] == 'v':
vcc = '0'+vcc
if vcc[-1] == 'v':
vcc = vcc+'0'
assert vcc[2] == 'v', vcc
return vcc
# -----
# -----
def _newname_transistor(d):
"""
>>> newname({
... 'device': 'bjt',
... 'esd': False,
... 'props': [],
... 'rf': False,
... 'subdevice': 'npn',
... 'vcc': '5v5',
... 'vrange': 'high',
... 'width': 1.0,
... 'length': 2.0,
... })
('npn_05v5', 'sky130_fd_pr__npn_05v5_W1p00L2p00')
>>> newname({
... 'device': 'bjt',
... 'esd': False,
... 'props': [],
... 'rf': True,
... 'subdevice': 'npn',
... 'vcc': '5v5',
... 'vrange': 'high',
... })
('rf_npn_05v5', 'sky130_fd_pr__rf_npn_05v5')
>>> newname({
... 'device': 'bjt',
... 'esd': False,
... 'parasitic': True,
... 'props': [],
... 'rf': False,
... 'subdevice': 'npn',
... 'vcc': '11v0',
... 'vrange': 'very',
... })
('npn_11v0', 'sky130_fd_pr__npn_11v0')
>>> newname({
... 'device': 'bjt',
... 'esd': False,
... 'props': [],
... 'rf': True,
... 'subdevice': 'npn',
... 'x': 1.0,
... 'y': 2.0,
... })
('rf_npn', 'sky130_fd_pr__rf_npn')
>>> newname({
... 'device': 'fet',
... 'esd': False,
... 'fingers': 4,
... 'length': 0.15,
... 'props': [],
... 'rf': False,
... 'subdevice': 'nfet',
... 'variant': 'a',
... 'vcc': '1v8',
... 'vrange': 'low',
... 'vt': 'low',
... 'width': 0.84,
... })
('nfet_01v8_lvt', 'sky130_fd_pr__nfet_01v8_lvt_aF04W0p84L0p15')
"""
# Front bit
# -----------------
basename = [d['subdevice']]
if d['rf']:
basename.insert(0, 'rf')
if d['esd']:
basename.insert(0, 'esd')
#d['vrange']
vcc = d.get('vcc', None)
if vcc:
basename.append(_newname_vcc(vcc))
else:
vrange = d.get('vrange', None)
assert not vrange, assert_pformat(vrange, '', vrange)
if vrange:
basename.append('v'+vrange)
vt = d.get('vt', None)
if vt:
basename.append(NEWNAME_VT[vt])
# d['props']
props = d.get('props', None)
if props:
basename.extend(p.replace(' ', '') for p in props if p not in ('base',))
basename = '_'.join(basename)
# Device Parameters
# -----------------
# d['width']
# d['length']
# d['multiple']
# d['fingers']
# d['variant']
params = []
variant = d.get('variant', None)
if variant:
params.append(variant)
multiple = d.get('multiple', None)
if multiple:
params.append('M' + _newname_i(multiple))
finger = d.get('fingers', None)
if finger:
params.append('F' + _newname_i(finger))
if 'width' in d:
params.append(_newname_wl(d))
#if 'a' in d:
# assert 'b' in d, d
# params.append('%1.0fx%1.0f' % (d['a'], d['b']))
# Full name construction
# -----------------
o = basename
if params:
o += '_' + (''.join(params))
return (basename, o)
# -----
# -----
# Diagram [N] - `ndiode` -- Intentional Diode between pwell and ndiff
#
# | | #
# | | #
# --+----+--+----^----+-----+----^----+--+-----+----- #
# | | | | | | | | #
# | | | P+ | | N+ | | | #
# | | | | | | | | #
# | | | | | [1] | | | #
# | | | | | ▁╻▁ | | | #
# | | +---------+ +---◢■◣---+ | | #
# | | ╹ | | #
# | | [2] P-Well | | #
# | | ╻ | | #
# | +--◥■◤--------------------------+ | #
# | ▔╹▔ | #
# | | #
# | [3] Deep N-Well | #
# | ▁╻▁ | #
# +----------------◢■◣-----------------------+ #
# ╹ #
# P Substrate #
# #
# --------------------------------------------------- #
#
# [1] - ndiode - pwell to ndiff -- intentional
# [2] - - pwell to deep-nwell -- parasitic
# -- xdnwdiode_pwell_rf
# -- xesd_dnwdiode_pw_X
# -- xesd_dnwdiode_pw_X
# -- xdnwdiode_pwell_rf
# -- dnwdiode_pw
# [3] - - psub to deep-nwell -- parasitic
# -- dnwdiode_psub
# Diagram [P] - `pdiode` -- Intentional Diode between pdiff and nwell
#
# | | #
# | | #
# -------+--+----^----+-----+----^----+--+-----+----- #
# | | | | | | #
# | | P+ | | N+ | | #
# | | | | | | #
# | | [1] | | | | #
# | | ╻ | | | | #
# | +---◥■◤---+ +---------+ | #
# | ▔╹▔ | #
# | [2] N-Well | #
# | ▁╻▁ | #
# +--◢■◣--------------------------+ #
# ╹ #
# P Substrate #
# #
# --------------------------------------------------- #
#
# [1] - pdiode - pdiff to nwell -- intentional
# [2] - nwdiode - psub to nwell -- parasitic
def _newname_diode_type(src, dst):
nsrc = NEWNAME_LAYERS[src]
assert 'p' in nsrc, (nsrc, (src, '->|-', dst))
ndst = NEWNAME_LAYERS[dst]
assert 'n' in ndst, (ndst, (src, '->|-', dst))
return "{}2{}".format(nsrc, ndst)
NEWNAME_DIODE_TYPE = {
'n diode' : _newname_diode_type('pwell', 'ndiff'),
'nwell diode' : _newname_diode_type('psub', 'nwell'),
'deep nwell diode -|<- pwell': _newname_diode_type('pwell', 'deep nwell'),
'deep nwell diode -|<- psub' : _newname_diode_type('psub', 'deep nwell'),
'p diode' : _newname_diode_type('pdiff', 'nwell'),
}
for v in NEWNAME_DIODE_TYPE.values():
src, dst = v.split('2')
assert src in NEWNAME_LAYERS.values(), (src, NEWNAME_LAYERS.values())
assert dst in NEWNAME_LAYERS.values(), (src, NEWNAME_LAYERS.values())
def _newname_pardiode(d):
"""
>>> newname({
... 'corners': [],
... 'device': 'pardiode',
... 'esd': True,
... 'for': {'corners': [],
... 'device': 'diode',
... 'esd': True,
... 'parasitic': False,
... 'props': [],
... 'rf': True,
... 'type': 'n diode',
... 'variant': '100',
... 'vcc': '11v0',
... 'vrange': 'high'},
... 'parasitic': True,
... 'props': [],
... 'rf': True,
... 'type': 'deep nwell diode -|<- pwell',
... })
('esd_rf_diode_pw2nd_11v0', 'sky130_fd_pr__esd_rf_diode_pw2nd_11v0_100__parasitic__diode_pw2dn')
>>> newname({
... 'device': 'pardiode',
... 'esd': False,
... 'for': {'device': 'fet',
... 'esd': False,
... 'props': [],
... 'rf': False,
... 'subdevice': 'nfet',
... 'vcc': '20v0',
... 'vrange': 'ultra',
... 'vt': 'native'},
... 'parasitic': True,
... 'props': [],
... 'rf': False,
... 'type': 'deep nwell diode -|<- psub',
... })
('nfet_20v0_nvt', 'sky130_fd_pr__nfet_20v0_nvt__parasitic__diode_ps2dn')
>>> newname({
... 'device': 'pardiode',
... 'esd': False,
... 'for': 'generic',
... 'parasitic': True,
... 'props': [],
... 'rf': False,
... 'type': 'deep nwell diode -|<- psub',
... 'corners': ['discrete'],
... })
('parasitics', 'sky130_fd_pr__model__parasitic__diode_ps2dn__discrete')
"""
assert d['device'] == 'pardiode', d
assert d['parasitic'], d
assert 'diff' not in d['type'], assert_pformat('diff not in', d['type'], d)
assert 'variant' not in d, pformat(d)
basename = []
basename.append('parasitic_')
basename.append('diode')
assert d['type'] in NEWNAME_DIODE_TYPE, d['type']
basename.append(NEWNAME_DIODE_TYPE[d['type']])
assert 'for' in d, d
if isinstance(d['for'], dict):
a, b = newname(d['for'])
assert b.startswith('sky130_fd_pr__'), (a, b)
#assert a == b, assert_pformat(a, b, d)
dirname = a
basename.insert(0, b.split('__', 1)[1]+'_')
else:
assert d['for'] == 'generic', assert_pformat(d['for'], 'generic', d)
dirname = 'parasitics'
if d['rf']:
basename.insert(-2, 'rf')
#if d['esd']:
# basename.insert(0, 'esd')
basename.insert(0, 'model_')
return (dirname, "_".join(basename))
# -----
# -----
def _newname_diode(d):
"""
>>> newname({
... 'esd': False,
... 'rf': False,
... 'device': 'diode',
... 'props': [],
... 'type': 'n diode',
... 'vcc': '5v5',
... 'vrange': 'high',
... 'vt': 'low',
... })
('diode_pw2nd_05v5_lvt', 'sky130_fd_pr__diode_pw2nd_05v5_lvt')
>>> newname({
... 'esd': False,
... 'rf': False,
... 'device': 'diode',
... 'props': [],
... 'type': 'p diode',
... 'vcc': '16v0',
... 'vrange': 'very',
... })
('diode_pd2nw_16v0', 'sky130_fd_pr__diode_pd2nw_16v0')
>>> newname({
... 'corners': [],
... 'device': 'diode',
... 'esd': True,
... 'parasitic': False,
... 'props': [],
... 'rf': False,
... 'type': 'n diode',
... 'variant': '100',
... 'vcc': '11v0',
... 'vrange': 'high',
... })
('esd_diode_pw2nd_11v0', 'sky130_fd_pr__esd_diode_pw2nd_11v0_100')
"""
assert d['device'] == 'diode', d
basename = ['diode']
if d['rf']:
basename.insert(0, 'rf')
if d['esd']:
basename.insert(0, 'esd')
assert d['type'] in ('n diode', 'p diode'), assert_pformat(d['type'], ('n diode', 'p diode'), d)
assert d['type'] in NEWNAME_DIODE_TYPE, assert_pformat(d['type'], NEWNAME_DIODE_TYPE, d)
basename.append(NEWNAME_DIODE_TYPE[d['type']])
vcc = d.get('vcc', None)
if vcc:
basename.append(_newname_vcc(vcc))
else:
vrange = d.get('vrange', None)
assert not vrange, assert_pformat('not', vrange, d)
if vrange:
basename.append('v'+vrange)
vt = d.get('vt', None)
if vt:
basename.append(NEWNAME_VT[vt])
params = []
variant = d.get('variant')
if variant:
params.append(variant)
dirname = "_".join(basename)
filename = dirname
if params:
filename += "_"+("".join(params))
return (dirname, filename)
def _newname_resistor(d):
"""
>>> newname({
... 'device': 'resistor',
... 'esd': False,
... 'layers': ['poly'],
... 'props': [],
... 'rf': False,
... 'size': 0.35,
... 'type': 'high',
... })
('res_high_po', 'sky130_fd_pr__res_high_po_0p35')
P diffusion Resistor
>>> newname({
... 'device': 'resistor',
... 'esd': False,
... 'layers': ['pdiff'],
... 'props': [],
... 'rf': False,
... 'type': 'generic',
... })
('res_generic_pd', 'sky130_fd_pr__res_generic_pd')
Isolated P-well resistor
>>> newname({
... 'device': 'resistor',
... 'esd': False,
... 'layers': ['pwell'],
... 'props': ['iso'],
... 'rf': False,
... 'type': 'generic',
... })
('res_generic_pw', 'sky130_fd_pr__res_generic_pw')
>>> newname({
... 'device': 'resistor',
... 'esd': False,
... 'layers': ['poly'],
... 'props': [],
... 'rf': False,
... 'type': 'generic',
... })
('res_generic_po', 'sky130_fd_pr__res_generic_po')
>>> newname({
... 'device': 'resistor',
... 'esd': False,
... 'layers': ['poly'],
... 'props': ['high sheet'],
... 'rf': False,
... 'size': 0.35,
... 'type': 'high',
... })
('res_high_po', 'sky130_fd_pr__res_high_po_0p35')
>>> newname({
... 'device': 'resistor',
... 'esd': False,
... 'example': '175320108',
... 'layers': ['poly'],
... 'props': [],
... 'rf': False,
... 'size': 0.35,
... 'type': 'high',
... })
('res_high_po', 'sky130_fd_pr__res_high_po_0p35__example1')
>>> newname({
... 'device': 'resistor',
... 'esd': False,
... 'example': '175323180',
... 'layers': ['li', 'm1'],
... 'props': [],
... 'rf': False,
... 'size': 0.35,
... 'type': 'high',
... })
('res_high_l1m1', 'sky130_fd_pr__res_high_l1m1_0p35__example1')
>>> newname({
... 'device': 'resistor',
... 'esd': False,
... 'layers': ['poly'],
... 'props': ['high sheet'],
... 'rf': False,
... 'size': 2.85,
... 'type': 'high',
... })
('res_high_po', 'sky130_fd_pr__res_high_po_2p85')
"""
assert d['device'] == 'resistor', d
basename = ['res']
if d['rf']:
basename.insert(0, 'rf')
if d['esd']:
basename.insert(0, 'esd')
assert 'type' in d
basename.append(d['type'])
assert 'layers' in d, d
layers = []
for s in d['layers']:
nl = NEWNAME_LAYERS[s]
if not nl:
continue
layers.append(nl)
layers.sort(key=layer_sort_key)
basename.append(''.join(layers))
params = []
size = d.get('size', None)
if size:
params.append(_newname_sf(size))
dirname = "_".join(basename)
filename = dirname
if params:
filename += "_"+("".join(params))
return (dirname, filename)
# -----
# -----
NEWNAME_CAP_VARIANT = {
'base' : 'base',
'base2' : 'base2',
'top' : 'top',
'subcell' : 'subcell',
'nwell' : 'nwell',
'raphael' : 'raphael',
'atlas' : 'a',
1 : '1',
2 : '2',
3 : '3',
'rcx' : 'x',
'rcxtop' : 'xtop',
'rcx6' : 'x6',
'rcx7' : 'x7',
'rcx8' : 'x8',
'rcx9' : 'x9',
'm5pullin': 'm5pullin',
'testcase': 'test',
'm5mod' : 'm5pullin',
'fom' : 'fom',
'finger' : 'fingercap',
'2finger' : 'fingercap2',
'basefinger' : 'basefingercap',
'base2finger' : 'basefingercap2',
'waffle' : 'wafflecap',
'nhv' : 'nhv',
'phv' : 'phv',
'topnhv' : 'nhvtop',
'topphv' : 'phvtop',
'2nhv' : '2nhv',
'2phv' : '2phv',
'2topnhv' : '2nhvtop',
'2topphv' : '2phvtop',
'raphaelnhv' : 'nhvraphael',
'raphaelphv' : 'phvraphael',
'raphael2nhv' : 'nhv2raphael',
'raphael2phv' : 'phv2raphael',
'old1' : 'o1',
'old1nhv' : 'o1nhv',
'old1phv' : 'o1phv',
'old1raphael' : 'o1raphael',
'old1nwell' : 'o1well',
'old1subcell' : 'o1subcell',
'old2' : 'o2',
'old2raphael' : 'o2raphael',
'old2nwell' : 'o2well',
'old2subcell' : 'o2subcell',
}
for k in list(NEWNAME_CAP_VARIANT.keys()):
if not isinstance(k, str):
NEWNAME_CAP_VARIANT[str(k)] = NEWNAME_CAP_VARIANT[k]
def _newname_capacitor(d):
"""
>>> newname({
... 'device': 'capacitor',
... 'esd': False,
... 'float': [],
... 'group': 'mim',
... 'metal': ['m3'],
... 'props': [],
... 'rf': False,
... 'shield': [],
... })
('cap_mim_m3', 'sky130_fd_pr__cap_mim_m3')
>>> newname({
... 'device': 'capacitor',
... 'esd': False,
... 'float': [],
... 'group': 'var',
... 'metal': [],
... 'props': [],
... 'rf': False,
... 'shield': [],
... 'vt': 'low',
... })
('cap_var_lvt', 'sky130_fd_pr__cap_var_lvt')
>>> newname({
... 'broken name?': True,
... 'device': 'capacitor',
... 'esd': False,
... 'float': [],
... 'group': 'vppcap',
... 'metal': ['li',
... 'm1',
... 'm2',
... 'm3',
... 'm4'],
... 'props': ['nhv', '2x', '10x4'],
... 'rf': True,
... 'shield': ['m5'],
... 'x': 11.34,
... 'y': 11.76,
... })
('cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5', 'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhv')
>>> newname({
... 'broken name?': True,
... 'device': 'capacitor',
... 'esd': False,
... 'float': [],
... 'group': 'vppcap',
... 'metal': ['m1', 'm2'],
... 'props': ['phv'],
... 'rf': False,
... 'shield': [],
... 'variant': 'old1',
... 'x': 4.38,
... 'y': 4.59,
... })
('cap_vpp_04p4x04p6_m1m2_noshield', 'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o1phv')
>>> newname({
... 'device': 'capacitor',
... 'esd': False,
... 'float': [],
... 'group': 'cap_int3',
... 'metal': ['m1', 'm2', 'm3', 'm4'],
... 'props': ['atlas', 'finger'],
... 'rf': False,
... 'shield': ['li'],
... 'x': 2.7,
... 'y': 41.1,
... })
('cap_vpp_02p7x41p1_m1m2m3m4_shieldl1', 'sky130_fd_pr__cap_vpp_02p7x41p1_m1m2m3m4_shieldl1_fingercap')
>>> newname({
... 'device': 'capacitor',
... 'esd': False,
... 'float': [],
... 'group': 'cap_int3',
... 'metal': ['poly',
... 'li',
... 'm1',
... 'm2',
... 'm3',
... 'm4',
... 'm5'],
... 'props': [],
... 'rf': False,
... 'shield': [],
... 'variant': 'testcase',
... 'x': 55.77,
... 'y': 23.09,
... })
('cap_vpp_55p8x23p1_pol1m1m2m3m4m5_noshield', 'sky130_fd_pr__cap_vpp_55p8x23p1_pol1m1m2m3m4m5_noshield_test')
>>> newname({
... 'device': 'capacitor',
... 'esd': False,
... 'group': 'vppcap',
... 'metal': ['m1'],
... 'props': [],
... 'rf': False,
... 'shield': ['m4', 'm5'],
... 'variant': 'raphael',
... 'x': 11.5,
... 'y': 11.7,
... })
('cap_vpp_11p5x11p7_m1_shieldm4m5', 'sky130_fd_pr__cap_vpp_11p5x11p7_m1_shieldm4m5_raphael')
>>> newname({
... 'device': 'capacitor',
... 'esd': False,
... 'group': 'vppcap',
... 'metal': ['m1', 'li'],
... 'props': [],
... 'rf': False,
... 'shield': ['m5'],
... 'variant': 'subcell',
... 'x': 115.0,
... 'y': 117.0,
... })
('cap_vpp_115p0x117p0_l1m1_shieldm5', 'sky130_fd_pr__cap_vpp_115p0x117p0_l1m1_shieldm5_subcell')
x115p0y117p0
sli_sm5
>>> newname({
... 'device': 'capacitor',
... 'esd': False,
... 'group': 'vppcap',
... 'metal': ['m1'],
... 'props': [],
... 'rf': False,
... 'shield': [],
... 'variant': 1,
... 'x': 115.0,
... 'y': 117.0,
... })
('cap_vpp_115p0x117p0_m1_noshield', 'sky130_fd_pr__cap_vpp_115p0x117p0_m1_noshield_1')
>>> newname({
... 'device': 'capacitor',
... 'esd': False,
... 'float': ['m4'],
... 'group': 'vppcap',
... 'metal': ['m1', 'm2', 'm3'],
... 'props': [],
... 'rf': False,
... 'shield': ['li', 'm5'],
... 'x': 4.38,
... 'y': 4.59,
... })
('cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4', 'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4')
"""
assert d['device'] == 'capacitor', d
basename = ['cap']
if d['esd']:
basename.insert(0, 'esd')
assert 'group' in d, d
if d['group'] == 'vppcap':
basename.append('vpp')
elif d['group'] == 'cap_int3':
basename.append('vpp')
elif d['group'] == 'mim':
basename.append('mim')
elif d['group'] == 'var':
basename.append('var')
vt = d.get('vt', None)
if vt:
basename.append(NEWNAME_VT[vt])
typename = list(basename)
return ("_".join(basename), "_".join(typename))
else:
raise ValueError("Unknown group:", d['group'])
if 'x' in d:
assert 'y' in d, d
basename.append(_newname_size(d))
assert 'metal' in d, d
assert d['metal'], d
#assert 'UmetalU' not in d['metal'], d
metals = []
for s in d['metal']:
nl = NEWNAME_LAYERS[s]
metals.append(nl)
metals.sort(key=layer_sort_key)
basename.append(''.join(metals))
if d['group'] != 'mim':
assert 'shield' in d, d
#assert 'UshieldU' not in d['shield'], d
shields = []
for s in d['shield']:
nl = NEWNAME_LAYERS[s]
if not nl:
continue
assert nl not in metals, (nl, metals, d)
shields.append(nl)
shields.sort(key=layer_sort_key)
if shields:
basename.append('shield'+''.join(shields))
else:
basename.append('noshield')
floats = []
for s in d.get('float', []):
nl = NEWNAME_LAYERS[s]
floats.append(nl)
if floats:
basename.append('float'+''.join(floats))
typename = list(basename)
if 'variant' in d:
v = d['variant']
else:
v = ''
if 'nhv' in d['props']:
v += 'nhv'
if 'phv' in d['props']:
v += 'phv'
if 'waffle' in d['props']:
v += 'waffle'
if 'finger' in d['props']:
v += 'finger'
if v:
typename.append(NEWNAME_CAP_VARIANT[v])
return ("_".join(basename), "_".join(typename))
# -----
# -----
def _newname_via(d):
"""
>>> newname({
... 'device': 'via',
... 'layers': ['m1', 'm2'],
... })
('via_m1m2', 'sky130_fd_pr__via_m1m2')
"""
assert d['device'] == 'via', d
basename = ['via']
assert 'layers' in d, d
layers = []
for s in d['layers']:
nl = NEWNAME_LAYERS[s]
if not nl:
continue
layers.append(nl)
layers.sort(key=layer_sort_key)
basename.append(''.join(layers))
typename = list(basename)
return ("_".join(basename), "_".join(typename))
# -----
# -----
def _newname_inductor(d):
"""
"""
assert d['device'] == 'inductor', d
basename = ['ind']
if 'y' in d:
basename.append(_newname_i(int(d['y'])))
if 'x' in d:
basename.append(_newname_i(int(d['x'])))
typename = list(basename)
if 'variant' in d:
typename.append(_newname_i(int(d['variant'])))
return ("_".join(basename), "_".join(typename))
# -----
# -----
def _newname_special(d):
"""
>>> newname({
... 'device': 'special',
... 'basename': 'XXXX',
... 'typename': 'sky130_fd_AA__BB',
... })
('XXXX', 'sky130_fd_AA__BB')
>>> newname({
... 'basename': 'special_nfet_pass_lvt',
... 'corners': ['ff', 'discrete'],
... 'device': 'special',
... 'esd': False,
... 'group': 'sram',
... 'key': 'nlvtpass',
... 'props': [],
... 'rf': False,
... 'subgroup': 'single',
... 'subtype': 'pass',
... 'type': 'n',
... 'typename': 'sky130_fd_pr__special_nfet_pass_lvt',
... 'vt': 'low',
... })
('special_nfet_pass_lvt', 'sky130_fd_pr__special_nfet_pass_lvt__ff_discrete')
"""
assert d['device'] == 'special', d
return d['basename'], d['typename']
# -----
# -----
def _newname_corners(d):
# Corners
# -----------------
# d['corners']
corners = d.get('corners', [])
corners = list(p.replace(' ', '') for p in corners)
if corners:
return '__' + ('_'.join(corners))
return ''
def newname(d):
"""
"""
if not d:
return (None, None)
if d['device'] in ('bjt', 'fet'):
s = _newname_transistor(d)
elif d['device'] in ('pardiode',):
s = _newname_pardiode(d)
elif d['device'] in ('diode',):
s = _newname_diode(d)
elif d['device'] in ('capacitor',):
s = _newname_capacitor(d)
elif d['device'] in ('resistor',):
s = _newname_resistor(d)
elif d['device'] in ('inductor',):
s = _newname_inductor(d)
elif d['device'] in ('special',) or 'basename' in d:
s = _newname_special(d)
elif d['device'] in ('via',):
s = _newname_via(d)
else:
assert False, "Unknown device type: {}\n{}".format(d['device'], pformat(d))
s = list(s)
if not s[1].startswith('sky130'):
s[1] = 'sky130_fd_pr__' + s[1]
if d.get('example', ''):
s[1] += '__example_'+d['example']
assert s[1] in EXAMPLE_CSV, s[1]
s[1] = EXAMPLE_CSV[s[1]]
s = (s[0], s[1] + _newname_corners(d))
return s
#################################################################
#################################################################
def get_headers(l):
h = set()
for d in l:
for k in d:
h.add(k)
return list(h)
# FET sort order
HEADERS = {
'fet': [
'src',
'libname',
'base',
'name',
'',
'subdevice', # nfet / pfet
'',
'esd', # esd
'rf', # rf
# Core properties?
'vcc', # Shouldn't matter
'vrange', # low, high, very, ultra
'vt', # empty, native, low, med, high
'props', # properties
'leftover', # ????
'',
'corners',
''
# Sizes
'width', #
'length', #
'multiple', # 1, 2, etc
'fingers', # ?
'variant', # a/b/c
# Extra?
],
}
CUSTOM_SORT_ORDER = {
'vt': [
'',
'native',
'zero',
'low',
'med',
'high',
],
'corners': [
'',
[],
['base'],
['f'],
['t'],
['s'],
['ff'],
['ff', 'discrete'],
['ff', 'bol'],
['ff', 'eol'],
['ff', 'teol'],
['tt'],
['tt', 'discrete'],
['tt', 'bol'],
['tt', 'eol'],
['tt', 'teol'],
['ss'],
['ss', 'discrete'],
['ss', 'bol'],
['ss', 'eol'],
['ss', 'teol'],
['fs'],
['fs', 'discrete'],
['sf'],
['sf', 'discrete'],
['mismatch'],
['subvt', 'mismatch'],
['leak'],
['leak', 'discrete'],
['tt', 'leak'],
['tt', 'leak', 'discrete'],
['tt', 'correln'],
['tt', 'correlp'],
['wafer'],
['wafer', 'discrete'],
['bol'],
['bol', 'mismatch'],
['tbol'],
['wbol'],
# FIXME:???
['debug'],
['fixed'],
['symbolic'],
['subcircuit'],
['discrete'],
['extended_drain'],
['hv'],
],
}
if __name__ == "__main__":
sys.stderr = sys.stdout
import doctest
fails, _ = doctest.testmod()
#if fails != 0:
# sys.exit("Some test failed")
if '--run' not in sys.argv:
sys.exit(fails)
data = defaultdict(lambda: list())
tests = open('decoder_tests.csv').read().splitlines()
tests.append('# Stripped extensions')
print('---')
i = 0
while i < len(tests):
ot = tests[i]
i += 1
if ',' not in ot:
continue
b = ot.split(',')
o = [b[0]]
for v in b[1:]:
if '_' in v:
v = v.rsplit('_', 1)[0]
o.append(v)
if o != b:
t = ','.join(o)
print('%4s' % i, len(tests), ['False', 'True '][t not in tests], '%-60s' % repr(ot), repr(t))
if t not in tests:
tests.append(t)
print('---')
tests.append('# Exported Capacitors')
for k in CAP_CUSTOM:
tests.append(','+k)
tests.append('# Exported BJT')
for k in BJT_CUSTOM:
tests.append(','+k)
tests.append('# Exported Diodes')
for k in DIODE_CUSTOM:
tests.append(','+k)
tests.append('# Exported Via Cells')
for k in VIA_CUSTOM:
tests.append(','+k)
tests.append('# Exported Resistors')
for k in RES_EXTRA:
if not k:
continue
tests.append(','+k)
tests.append('# Exported Special Cells')
for k in SPECIAL_CUSTOM:
tests.append(','+k)
extra_tests = set()
def add_new_test(bits, name):
new_test = ",".join((bits[0], name, bits[2]))
if new_test in tests:
return
extra_tests.add(new_test)
for line in tests:
bits = line.split(',')
if len(bits) < 3:
continue
if bits[1].endswith('1') and not bits[1].endswith('x1'):
add_new_test(bits, bits[1][:-1])
if "1_" in bits[1] and 'x1_' not in bits[1]:
add_new_test(bits, bits[1].replace("1_", "_"))
if '_' not in bits[1]:
continue
e = bits[1].split('_')
while len(e) > 1:
e.pop(-1)
add_new_test(bits, "_".join(e))
extra_tests = list(sorted(extra_tests))
tests.append('# Extra generated tests')
tests.extend(extra_tests)
renames = {}
errors_newname = defaultdict(list)
errors_parse = defaultdict(list)
for line in tests:
if not line.strip():
continue
if line.startswith('#'):
assert line.strip(), repr(line)
print()
print()
print()
print('#'*200)
print('#'*200)
print('#', line[1:].strip().ljust(196), '#')
print('#'*200)
print('#'*200)
print()
continue
line = line.strip()
if ',' not in line:
print('Skipping:', line)
continue
libname, cellname = line.split(',', 1)
ext = ''
if ',' in cellname:
cellname, ext = cellname.split(',', 1)
name = (None, None)
leftover = '?'
try:
info, leftover = parse_name(cellname)
if not info:
print("Parser return no results for **existing** name for", repr(cellname), "(from", repr(line)+')')
errors_parse[cellname].append((line, 'No result'))
continue
info['src'] = line
if 'leftover' not in info:
info['leftover'] = leftover
if libname:
info['libname'] = libname
try:
name = newname(info)
if name[0]:
cell_basename, cell_fullname = name
info['base'] = cell_basename
info['name'] = cell_fullname
assert cell_fullname.startswith('sky130_fd_pr__') or cell_fullname.startswith('sky130_fd_bs_flash__'), cell_fullname
dirs = common.directory_for_cell(cell_fullname)
renames[cellname.lower()] = '{libname}/{d0}/{cell_basename}/{cell_fullname}'.format(
libname = 'sky130_fd_pr',
d0 = dirs[0],
cell_basename = cell_basename,
cell_fullname = cell_fullname,
)
except Exception as e:
errors_newname[cellname].append((line, str(e)))
print()
print("Unable to create **new name** for", repr(line))
print('-'*10)
traceback.print_exc(file=sys.stdout)
print(file=sys.stderr, flush=True)
print('-'*10)
data[info['device']].append(info)
if info['device'] == 'pardiode':
data['diode'].append(dict(info))
except Exception as e:
errors_parse[cellname].append((line, str(e)))
print()
print("Unable to parse ", repr(line))
print('-'*10)
traceback.print_exc(file=sys.stdout)
print('-'*10)
print()
continue
print('Decoded', "%-40r" % (cellname,), '%-20r' % (leftover,), '%-60r %r' % name)
print('#'*200)
print('#'*200)
print('Finished decode')
print()
print()
print("Errors")
print('#'*200)
print("Parsing")
print('-'*10)
errors_parse = list(errors_parse.items())
errors_parse.sort()
pprint.pprint(errors_parse, width=200)
print('-'*10)
print("New name")
print('-'*10)
errors_newname = list(errors_newname.items())
errors_newname.sort()
pprint.pprint(errors_newname, width=200)
print('-'*10)
print()
for device in data:
print("%20s" % device, end=" ", flush=True)
headers = get_headers(data[device])
headers.remove('device')
headers.sort()
if device in HEADERS:
cheaders = HEADERS[device]
for h in cheaders:
if h == '':
continue
headers.remove(h)
assert len(headers) == 0, headers
headers = cheaders
else:
has_subdevice = 'subdevice' in headers
# Put these at the start
headers.remove('src')
if 'base' in headers:
headers.remove('base')
if 'name' in headers:
headers.remove('name')
if 'libname' in headers:
headers.remove('libname')
if has_subdevice:
headers.remove('subdevice')
headers.insert(0, 'src')
headers.insert(1, 'libname')
headers.insert(2, 'base')
headers.insert(3, 'name')
headers.insert(4, '')
if has_subdevice:
headers.insert(5, 'subdevice')
# esd
# rf
# Put these at the end
headers.append('')
if 'corners' in headers:
headers.remove('corners')
headers.append('corners')
if 'props' in headers:
headers.remove('props')
headers.append('props')
if 'leftover' in headers:
headers.remove('leftover')
headers.append('leftover')
d = data[device]
for r in d:
del r['device']
if 'props' in r and 'base' in r['props']:
r['props'].remove('base')
def sk(d):
o = []
assert headers[0] == 'src', headers
assert headers[1] == 'libname', headers
assert headers[2] == 'base', headers
if 'base' not in d:
o.append('')
else:
o.append(d['base'])
assert headers[3] == 'name', headers
if 'name' not in d:
o.append('')
else:
o.append(d['name'])
assert headers[4] == '', headers
for h in headers[5:]:
if not h:
v = ''
elif h not in d:
v = ''
else:
v = d[h]
if h in CUSTOM_SORT_ORDER:
v = CUSTOM_SORT_ORDER[h].index(v)
assert v != -1, (h, v, CUSTOM_SORT_ORDER[h])
elif isinstance(v, float):
v = '%010.6f' % v
elif isinstance(v, int):
v = '%010d' % v
elif isinstance(v, (tuple, list)):
v = '%10d-%r' % (len(v), repr(v))
elif not isinstance(v, str):
v = repr(v)
elif h == 'vcc':
if not v:
v = '00v0'
elif v[1] == 'v':
v = '0'+v
o.append(v)
return tuple(o)
print('Headers:', headers)
d.sort(key=sk)
with open('devices-{}s.tsv'.format(device), 'w', newline='') as f:
w = csv.DictWriter(f, headers, delimiter='\t', quoting=csv.QUOTE_NONNUMERIC)
w.writeheader()
lr = None
for r in d:
if r == lr:
continue
w.writerow(r)
lr = r
counts = defaultdict(lambda: 0)
for k, v in renames.items():
counts[v] += 1
def bits(v):
if v.startswith('sky130_fd_pr/cells/'):
v = v[len('sky130_fd_pr/cells/'):]
elif v.startswith('sky130_fd_pr/models/'):
v = v[len('sky130_fd_pr/models/'):]
else:
return v
return v.replace('/', ',')
import spice_rewrite
with open('rewrites.csv', 'w') as f:
f.write('from,to\n')
for k, v in list(sorted(renames.items(), key=lambda x: (-len(x[0]), x[0]))):
f.write('{},{}\n'.format(k, bits(v).split(',')[-1]))
for k, v in spice_rewrite.FILE_MAPPING.items():
renames[k] = 'sky130_fd_pr/'+v
renames[k.split('.', 1)[0]] = 'sky130_fd_pr/'+v
with open('names2files-a.csv', 'w') as f:
f.write('name,output file,dir name,file name\n')
for k, v in list(sorted(renames.items(), key=lambda x: (-len(x[0]), x[0]))):
f.write('{},{},{}\n'.format(k, v, bits(v)))
with open('names2files-b.csv', 'w') as f:
f.write('name,output file,files,dir name,file name\n')
for k, v in list(sorted(renames.items(), key=lambda x: (x[1], x[0]))):
f.write('{},{},{},{}\n'.format(k, v, counts[v], bits(v)))
subprocess.call('./upload-sheet-audit-data.py')
subprocess.call('./upload-devices.py')