| #!/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') |