blob: ebb37b9101329b18b2e7ff242da43c6844ada72e [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2020 The Skywater PDK Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import csv
import enum
import io
import json
import os
import pathlib
import pprint
import random
import re
import string
import subprocess
import sys
import textwrap
import time
import traceback
from shutil import copyfile
from collections import defaultdict, namedtuple
import decoder
EFS8_RE = re.compile('efs8(hd|hdll|hs|ms|ls|hvl)')
global_debug = lambda *a, **kw: None
copyright_header = {}
copyright_header['base'] = """\
Copyright 2020 The SkyWater PDK Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
"""
def create_copyright_header(prefix, midfix, suffix):
output = []
if prefix:
output.append(prefix)
output.extend((midfix+l).rstrip() for l in copyright_header['base'].splitlines())
if suffix:
output.append(suffix)
output.append('')
output.append('')
return "\n".join(output)
copyright_header['/**/'] = create_copyright_header('/*', ' * ', '*/')
copyright_header['//'] = create_copyright_header('', '// ', '')
copyright_header['#'] = create_copyright_header('', '# ', '')
copyright_header['*'] = create_copyright_header('', '* ', '')
LOGICS = [
' | ',
' & ',
' OR ',
' AND ',
' NOR ',
' NAND ',
'( ',
' )',
' of ',
' input ',
' output ',
'input ',
'output ',
'of ',
' input',
' output',
' of',
]
DONT_BREAK = {}
for l in LOGICS:
DONT_BREAK[l] = l.replace(' ', '\u00a0')
# Convert whitespace inside equations to non-breaking.
# \u00a0
#
# '2-input AND into first input of 4-input OR ((A1 & A2) | B1 | C1 | D1)'
def whitespace_convert(s):
for fs, ts in DONT_BREAK.items():
s = s.replace(fs, ts)
return s
def whitespace_revert(s):
for fs, ts in DONT_BREAK.items():
s = s.replace(ts, fs)
return s
def wrap(s, i=''):
s = whitespace_convert(s)
p = ' * '+i
m = ' * '+(' '*len(i))
s = "\n".join(textwrap.wrap(
s,
initial_indent=p,
subsequent_indent=m,
break_on_hyphens=False,
expand_tabs=True,
))
return whitespace_revert(s)
def cleanup_module_name(mod):
"""
>>> cleanup_module_name('efs8hd')
'scs8hd'
>>> cleanup_module_name('blah.123')
'blah'
>>> cleanup_module_name('abc$213')
'abc'
>>> cleanup_module_name('cds_thru')
'feedthru'
>>> cleanup_module_name('cds_thru_cds_alias')
'feedthru_netalias'
>>> cleanup_module_name('BLAH')
'blah'
>>> cleanup_module_name('s8pir_10r_vcells_lvs')
'vcells_lvs'
"""
# Rewrite 'efs8'
mod = EFS8_RE.sub('scs8\\1', mod)
if '$' in mod:
mod = mod[:mod.find('$')]
if '.' in mod:
mod = mod[:mod.find('.')]
if mod == "cds_thru":
mod = "feedthru"
if mod == "cds_thru_cds_alias":
mod = "feedthru_netalias"
if 'cds_' in mod:
mod = mod.replace('cds_', '')
if mod.startswith('s8pir_10r_'):
mod = mod[len('s8pir_10r_'):]
if mod in ('primdev', 'pads'):
return None
return mod.lower()
modules = defaultdict(set)
modules_inc = defaultdict(set)
def add_file_for_module(mod, pn, include=False):
assert mod, ("-->", mod, pn)
assert pn, (mod, "-->", pn)
libname, modname = lib_extract_from_name(mod)
modname = cleanup_module_name(modname)
if include:
print(" Includes", end=" ")
modules_inc[modname].add(pn)
else:
assert \
(not libname) or \
(libname in pn) or \
(libname in pn.replace('efs8', 'scs8')) or \
(libname in pn.replace('s8iom0/', 's8iom0s8/')) or \
"primdev" in pn or \
False, (libname, modname, mod, pn)
print(" Adding ", end=" ")
modules[modname].add(pn)
print(repr(modname), end=" ")
if mod != modname:
print("({!r})".format(mod), end=" ")
print("in", repr(pn))
SCS8_RE = re.compile('^scs8[^_]*(_|$)')
S8_RE = re.compile('^s8[^_]+(_|$)')
WEIRD_LIBS = [
'efs8',
"s8_esd",
's8phirs_10r',
"efab_lib",
"s8_spice_models",
]
def lib_extract_from_name(mod):
"""
>>> lib_extract_from_name('abc')
(None, 'abc')
>>> lib_extract_from_name('a_s8_c')
(None, 'a_s8_c')
>>> lib_extract_from_name('s8_blah_c')
(None, 's8_blah_c')
>>> lib_extract_from_name('scs8hvl_abc')
('scs8hvl', 'abc')
>>> lib_extract_from_name('s8rf_random')
('s8rf', 's8rf_random')
>>> lib_extract_from_name('s8rf2_xcmvpp11p5x11p7_m4shield')
('s8rf2', 's8rf2_xcmvpp11p5x11p7_m4shield')
>>> lib_extract_from_name('efs8_pads')
('efs8', 'pads')
>>> lib_extract_from_name('scs8hd_xor3_4')
('scs8hd', 'xor3_4')
>>> lib_extract_from_name('s8iom0s8_abc')
('s8iom0s8', 'abc')
>>> lib_extract_from_name('s8_esd_Li_40K_2_RevA_south')
('s8_esd', 'Li_40K_2_RevA_south')
>>> lib_extract_from_name('s8_esd_gnd2gnd_120x2_lv_isosub')
('s8_esd', 'gnd2gnd_120x2_lv_isosub')
>>> lib_extract_from_name('s8blerf_rx_au_LNA_ind_shield_m1')
(None, 's8blerf_rx_au_LNA_ind_shield_m1')
>>> # was ('s8blerf', 'rx_au_LNA_ind_shield_m1')
>>> lib_extract_from_name('s8phirs_10r_x')
('s8phirs_10r', 'x')
>>> lib_extract_from_name('efs8hd_a211oi_2')
('scs8hd', 'a211oi_2')
>>> lib_extract_from_name('scs8hs')
('scs8hs', None)
>>> lib_extract_from_name('')
(None, '')
>>> lib_extract_from_name(None)
(None, None)
>>> lib_extract_from_name('s8blref_xind4_01')
(None, 's8blref_xind4_01')
SRAM cells
>>> lib_extract_from_name('s8Dp_blkinv_opt1')
('s8sram', 's8Dp_blkinv_opt1')
>>> lib_extract_from_name('s8sram16x16_colend_p_cent')
('s8sram', 's8sram16x16_colend_p_cent')
>>> lib_extract_from_name('s8sram_colend_cent')
('s8sram', 's8sram_colend_cent')
>>> lib_extract_from_name('sram_cell_1rw_1r')
('s8sram', 'sram_cell_1rw_1r')
>>> lib_extract_from_name('sram_dff')
('s8sram', 'sram_dff')
>>> lib_extract_from_name('libcell_scs8hvl')
('scs8hvl', 'libcell')
>>> lib_extract_from_name('libcell_scs8ls')
('scs8ls', 'libcell')
>>> lib_extract_from_name('latchupcell_scs8hvl')
('scs8hvl', 'latchupcell')
>>> lib_extract_from_name('latchupcell_scs8ls')
('scs8ls', 'latchupcell')
"""
if not mod:
return (None, mod)
if 's8Dp' in mod:
return ('s8sram', mod)
elif 'sram' in mod:
return ('s8sram', mod)
if mod.startswith('libcell_scs8') or mod.startswith('latchupcell_scs8'):
ext_cellname, ext_libname = mod.split('_', 1)
return (ext_libname, ext_cellname)
mod = EFS8_RE.sub('scs8\\1', mod)
if mod.startswith('scs8'):
m = SCS8_RE.search(mod)
if m:
lib = m.group(0)
if lib.endswith('_'):
return (lib[:-1], mod[len(lib):])
else:
return (lib, None)
if mod.startswith('s8rf2'):
return ('s8rf2', mod)
if mod.startswith('s8rf'):
return ('s8rf', mod)
if mod.startswith('s8') and not mod.startswith('s8p') and not mod.startswith('s8bl'):
m = S8_RE.search(mod)
if m:
lib = m.group(0)
if lib.endswith('_'):
return (lib[:-1], mod[len(lib):])
else:
return (lib, None)
for l in WEIRD_LIBS:
if not mod.startswith(l+'_'):
continue
return (l, mod[len(l)+1:])
return (None, mod)
VERSION_RE = re.compile('[vV]([0-9]+)\.([0-9]+)\.([0-9I])')
def version_extract_from_path(pn):
"""
>>> version_extract_from_path("/home/tansell/github/google/skywater-pdk/s8/V1.2.1/VirtuosoOA/libs/s8rf/s8rf_nhv_W3p0_L0p5_M10_b/extracted/master.tag")
(1, 2, 1)
>>> version_extract_from_path("/home/tansell/github/google/skywater-pdk/s8/v1.2.1/VirtuosoOA/libs/s8rf/s8rf_nhv_W3p0_L0p5_M10_b/extracted/master.tag")
(1, 2, 1)
>>> version_extract_from_path("/home/tansell/github/google/skywater-pdk/scs8hd/V0.0.I/verilog/scs8hd_sdlclkp_2.v")
(0, 0, 'I')
>>> version_extract_from_path("/ssd/gob/foss-eda-tools/skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/tech/bkeep_p/verilog/verilog.v")
(1, 0, 0)
"""
m = VERSION_RE.search(pn)
if not m:
return None
p1 = int(m.group(1))
p2 = int(m.group(2))
p3 = m.group(3)
try:
p3 = int(p3)
except ValueError:
pass
return (p1, p2, p3)
Corner = namedtuple("Corner", "mod volts temps extra")
class CornerFlag(enum.Flag):
t = 'Typical' # all nominal (typical) values
f = 'Fast' # fast, that is, values that make transistors run faster
s = 'Slow' # slow
# "fs" is sometimes called "wp" meaning worst-case power,
# "sf" is sometimes called "ws" meaning worst-case speed
nointpr = 'No internal power'
lv = 'Low voltage'
ccsnoise = 'Composite Current Source Noise'
VOLTS_RE = re.compile('([\-pn+])?([0-9]+[.p][0-9]+)([vV]|lv)')
TEMP_RE = re.compile('([\-pn+])?([0-9]+)[Cc]')
def _sign(c, v):
if c is None:
return v
assert len(c) == 1, c
if c in '+p':
return v
elif c in '-n':
return -v
else:
raise ValueError('Unknown sign value:', repr(c))
def corners_extract_from_filename_lib(fn):
"""
>>> corners_extract_from_filename('s8iom0s8_top_sio_macro_ffss_1.95v_1.65v_-40C.lib')
('s8iom0s8', Corner(mod='top_sio_macro', volts=(1.95, 1.65), temps=(-40.0,), extra=('ffss',)))
>>> corners_extract_from_filename('s8iom0s8_top_pwrdetv2_ssff_1.60v_5.50v_1.65v_-40C.lib')
('s8iom0s8', Corner(mod='top_pwrdetv2', volts=(1.6, 5.5, 1.65), temps=(-40.0,), extra=('ssff',)))
# FIXME: Check with Tim Edwards
>>> corners_extract_from_filename('scs8hvl_tt_3.3v_lowhv_3.3v_lv_1.8v_100C.lib')
('scs8hvl', Corner(mod=None, volts=(3.3, 'lowhv', 3.3, 'lv', 1.8), temps=(100.0,), extra=('tt',)))
>>> corners_extract_from_filename('s8iom0s8_top_amuxsplitv2_ff_ff_1p95v_x_1p65v_100C.lib')
('s8iom0s8', Corner(mod='top_amuxsplitv2', volts=(1.95, 'x', 1.65), temps=(100.0,), extra=('ff', 'ff')))
>>> corners_extract_from_filename('s8iom0s8_top_axresv2_tt_1.80v_3.30v_3.30v_025C.lib')
('s8iom0s8', Corner(mod='top_axresv2', volts=(1.8, 3.3, 3.3), temps=(25.0,), extra=('tt',)))
>>> corners_extract_from_filename('s8iom0s8_top_gpio_ovtv2_ff_ff_1p95v_x_5p50v_n40C_nointpwr.lib')
('s8iom0s8', Corner(mod='top_gpio_ovtv2', volts=(1.95, 'x', 5.5, 'nointpwr'), temps=(-40.0,), extra=('ff', 'ff')))
>>> corners_extract_from_filename('scs8hs_ss_1.60v_-40C_ccsnoise.lib')
('scs8hs', Corner(mod=None, volts=(1.6,), temps=(-40.0,), extra=('ss', 'ccsnoise')))
>>> corners_extract_from_filename('scs8hdll_ff_xx_1p56lv_hv_io_n40c_lrhc_tpl_PVT2VASTA20.lib')
('scs8hdll', Corner(mod='xx_io_lrhc_tpl_PVT2VASTA20', volts=(-1.56, 'lv', 'hv'), temps=(-40.0,), extra=('ff',)))
"""
assert fn.endswith('.lib'), fn
base, ext = os.path.splitext(fn)
assert ext == '.lib', (base, ext, fn)
bits = base.split('_')
extras = []
unknown = []
voltages = []
temps = []
libcellspec = []
while len(bits) > 0:
last = bits.pop(-1)
if last == '1p56lv':
# HACK for scs8hdll/V0.1.0/lib/scs8hdll_ff_xx_1p56lv_hv_io_n40c_lrhc_tpl_PVT2VASTA20.lib
voltages.insert(0, 'lv')
voltages.insert(0, _sign(sign, 1.56))
continue
v = VOLTS_RE.match(last)
if v:
sign, value, unit = v.groups()
value = value.replace('p', '.')
value = value.strip('0')
if unit == 'lv':
voltages.insert(0, unit)
voltages.insert(0, _sign(sign, float(value)))
continue
# FIXME: Are these correct!?
if last in ('x', 'lv', 'hv', 'lowhv', 'nointpwr'):
voltages.insert(0, last)
# HACK for scs8hdll/V0.1.0/lib/scs8hdll_ff_xx_1p56lv_hv_io_n40c_lrhc_tpl_PVT2VASTA20.lib
assert VOLTS_RE.match(bits[-1]) or TEMP_RE.match(bits[-1]) or bits[-1] == '1p56lv', (bits[-1], last, bits[:-1])
continue
t = TEMP_RE.match(last)
if t:
sign, value = t.groups()
temps.insert(0, _sign(sign, float(value)))
continue
if last in ['ff', 'ssff', 'ffss', 'ss', 'tt', 'ccsnoise', '5ns', 'pwr', 'lkg']:
extras.insert(0, last)
continue
# unknown = bits+[last,]
# break
libcellspec.insert(0, last)
mergedextras = '_'.join(extras)
libname, modname = lib_extract_from_name('_'.join(libcellspec))
# mergedextras = mergedextras.replace(modname, '')
# mergedextras = mergedextras.replace(libname, '')
extras = [el for el in mergedextras.split('_') if el != '']
return libname, Corner(mod=modname, extra=tuple(extras), volts=tuple(voltages), temps=tuple(temps))
def explain(modname):
"""
a41oi_4
"41" probably means 4-input, with 1 input inverted.
(The only real way to tell what it is is to look at the "function" line in a liberty file.)
the _4 refers to output drive strength. Relative to 1x.
aoi = and-or-invert.
oai = or-and-invert. (i.e., AND gates combined into a NOR gate, or OR gates combined into a NAND gate).
a2111oi_2
- 2111 refers to the AND part of AOI being 2 inputs,
- followed by 111, the three OR inputs.
I don't know if that is a universal standard, or if it really can work in all cases.
a41oi then I guess is (A1&A2&A3&A4) | (B1).
- That it is a two-output function is very unusual.
"b" refers to an inverted input. So a2bb2o_2 is an AND OR, 2 of the inputs are inverted
"b" is used in the flops, too, like "dfbbn".
- "r", "s", "t", "b" for the sets and resets
"""
CORNERS = [
'f', # Fast
's', # Slow
't', # Typical
'ff', # Fast Fast
'fs', # Fast Slow
'sf', # Slow Fast
'ss', # Slow Slow
'tt', # Typical Typical
'leak',
'ttcorreln', # Typical Typical + correlated + n?
'ttcorrelp', # Typical Typical + correlated + n?
'b',
'base', # Should this be `base_b`?
'wafer',
'nonfet',
'discrete',
'subvtmm',
'mm',
'hvt', # High Voltage?
'lvt', # Low Voltage?
'h',
'native',
'subcircuit',
'rf',
]
def corners_extract_from_filename_cor(fn):
"""
>>> corners_extract_from_filename('nshort_rf_base_b.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'rf')))
>>> corners_extract_from_filename('nshort_rf_base_b_ff.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'ff', 'rf')))
>>> corners_extract_from_filename('nshort_rf_base_b_fs.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'fs', 'rf')))
>>> corners_extract_from_filename('nshort_rf_base_b_leak.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'leak', 'rf')))
>>> corners_extract_from_filename('nshort_rf_base_b_sf.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'rf', 'sf')))
>>> corners_extract_from_filename('nshort_rf_base_b_tt.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'rf', 'tt')))
>>> corners_extract_from_filename('nshort_rf_base_b_tt_leak.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'leak', 'rf', 'tt')))
>>> corners_extract_from_filename('nshort_rf_base_b_ttcorreln.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'rf', 'ttcorreln')))
>>> corners_extract_from_filename('nshort_rf_base_b_ttcorrelp.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'rf', 'ttcorrelp')))
>>> corners_extract_from_filename('nshort_rf_base_b_wafer.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('b', 'base', 'rf', 'wafer')))
>>> corners_extract_from_filename('nshort_rf_mm.cor')
(None, Corner(mod='nshort', volts=(), temps=(), extra=('mm', 'rf')))
>>> corners_extract_from_filename('ff_subvtmm.cor')
(None, Corner(mod='', volts=(), temps=(), extra=('ff', 'subvtmm')))
>>> corners_extract_from_filename('ff_nonfet.cor')
(None, Corner(mod='', volts=(), temps=(), extra=('ff', 'nonfet')))
>>> corners_extract_from_filename('ff_discrete.cor')
(None, Corner(mod='', volts=(), temps=(), extra=('discrete', 'ff')))
>>> corners_extract_from_filename('ndiode.cor')
(None, Corner(mod='ndiode', volts=(), temps=(), extra=()))
>>> corners_extract_from_filename('ndiode_h.cor')
(None, Corner(mod='ndiode', volts=(), temps=(), extra=('h',)))
>>> corners_extract_from_filename('ndiode_hvt.cor')
(None, Corner(mod='ndiode', volts=(), temps=(), extra=('hvt',)))
>>> corners_extract_from_filename('ndiode_lvt.cor')
(None, Corner(mod='ndiode', volts=(), temps=(), extra=('lvt',)))
>>> corners_extract_from_filename('ndiode_native.cor')
(None, Corner(mod='ndiode', volts=(), temps=(), extra=('native',)))
>>> corners_extract_from_filename('nvhv_subcircuit.cor')
(None, Corner(mod='nvhv', volts=(), temps=(), extra=('subcircuit',)))
>>> corners_extract_from_filename('ss_discrete.cor')
(None, Corner(mod='', volts=(), temps=(), extra=('discrete', 'ss')))
"""
assert fn.endswith('.cor'), fn
base, ext = os.path.splitext(fn)
assert ext == '.cor', (base, ext, fn)
bits = base.split('_')
other = []
extra = []
while len(bits) > 0:
b = bits.pop(-1)
if b in CORNERS:
extra.insert(0, b)
else:
other.insert(0, b)
extra.sort()
return None, Corner(mod="_".join(other), extra=tuple(extra), volts=tuple(), temps=tuple())
def corners_extract_from_filename(fn):
if fn.endswith('.lib'):
return corners_extract_from_filename_lib(fn)
elif fn.endswith('.cor'):
return corners_extract_from_filename_cor(fn)
else:
raise ValueError("Don't know how to process"+fn)
#nlowvt_w1p65_l0p25_m4
def _libnames_fixup(libnames):
def _remove(s, o=None):
if s not in libnames:
return
if o and o not in libnames:
return
libnames.remove(s)
_remove(None)
# Always remove...
_remove('efab_lib')
_remove('scs8lpa')
_remove('s8-generic')
_remove('s8gds')
_remove('s8io_m0s8')
_remove('scs8')
_remove('techLEF')
# Remove if better name exists...
_remove('efs8', 'efs8_pads')
_remove('s8-generic', 's8x')
_remove('s8io', 's8iom0s8')
_remove('s8iom0', 's8iom0s8')
_remove('s8iom0s8', 's8_esd')
_remove('s8iom0s8', 's8rf')
_remove('s8iom0s8', 's8rf2')
# FIXME: check this
_remove('s8blerf', 'primdev')
_remove('s8blref', 'primdev')
_remove('primdev', 's8rf')
_remove('primdev', 's8rf2')
_remove('s8Dp', 's8sram')
_remove('s8sram16x16', 's8sram')
to_remove = set()
for l in libnames:
if l.endswith('_dv'):
to_remove.add(l)
if l.endswith('.oa'):
to_remove.add(l)
for l in libnames:
if not l.startswith('scs8_'):
continue
oname = 'scs8'+l[4:]
if oname in libnames:
to_remove.add(l)
for l in to_remove:
libnames.remove(l)
PATHS = [
"models",
#'liberty',
'cdl',
'gds',
'hspice',
'lef',
'mag',
'maglef',
'spi',
'spice',
'sue',
'techLEF',
'verilog',
]
def mod_extract_from_path(pn):
"""
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/libs/s8phirs_10r/L1_T/symbolic/layout.oa')
('s8phirs_10r', 'l1_t')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/MODELS/SPECTRE/s8x/Models/sonos_ffeol.cor')
('s8x', 'sonos_ffeol')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/libs/s8rf/nshort_W1p65_L0p15_M4/data.dm')
('s8rf', 'nshort_w1p65_l0p15_m4')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/MODELS/SPECTRE/s8phirs_10r/examples/bjt_netlist.scs')
(None, None)
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/libs/display.drf')
(None, None)
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/libs/tech/zfilter/verilog/symbol.oa')
('s8', 'zfilter')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/libs/technology_library/CAPMM4/symbolic/master.tag')
('s8', 'capmm4')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/lib/s8iom0s8_top_sio_macro_ffss_1.95v_1.65v_-40C.lib')
('s8iom0s8', 'top_sio_macro')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/lib/s8iom0s8_top_xres4v2_ff_ss_1p95v_x_1p95v_100C.lib')
('s8iom0s8', 'top_xres4v2')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8_esd/s8_esd_Li_40K_2_RevA_south/layout/master.tag')
('s8_esd', 'li_40k_2_reva_south')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8_esd/s8_esd_gnd2gnd_120x2_lv_isosub/symbol/symbol.oa')
('s8_esd', 'gnd2gnd_120x2_lv_isosub')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8iom0s8/s8iom0s8_amx_xor/schematic/data.dm')
('s8iom0s8', 'amx_xor')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8iom0s8_dv/s8rf_xcmvpp8p6x7p9_m3shield/layout/layout.oa')
('s8rf', 's8rf_xcmvpp8p6x7p9_m3shield')
>>> mod_extract_from_path('/home/tansell/projects/efabless/tech/SW/EFS8A/libs.ref/efs8_pads/mag/efs8_pads.mag')
('efs8_pads', None)
>>> mod_extract_from_path('/home/tansell/projects/efabless/tech/SW/EFS8A/libs.ref/efs8_pads/mag/analog200ohm_pad.mag')
('efs8_pads', 'analog200ohm_pad')
>>> mod_extract_from_path('/home/tansell/projects/efabless/tech/SW/EFS8A/libs.ref/primdev/mag/s8blerf_rx_au_LNA_ind_shield_m1.mag')
('primdev', 's8blerf_rx_au_lna_ind_shield_m1')
>>> # Was ('primdev', 'rx_au_lna_ind_shield_m1')
>>> mod_extract_from_path('/home/tansell/projects/efabless/tech/SW/EFS8A/libs.ref/primdev/mag/s8blref_xind4_01.mag')
('primdev', 's8blref_xind4_01')
>>> # Was ('primdev', 'xind4_01')
>>> mod_extract_from_path('/home/tansell/gob/tims-openpdk-repo/s8/libraries/s8_osu130/sue/MUX2X1.sue')
('s8_osu130', 'mux2x1')
>>> mod_extract_from_path('/home/tansell/gob/tims-openpdk-repo/s8/libraries/efs8hd/lef/efs8hd_a211oi_2.lef')
('scs8hd', 'a211oi_2')
>>> mod_extract_from_path('/home/tansell/gob/ef2/s8spice/home/uzmn/s8/cypress_models/spice/correl3.cor')
('s8spice', 'correl3')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.2.0/verilog/s8iom0s8_top_ground_hvc_wpad.v')
('s8iom0s8', 'top_ground_hvc_wpad')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/libs/s8phirs_10r/bkeep_p/verilog/verilog.v')
('s8phirs_10r', 'bkeep_p')
>>> mod_extract_from_path('/home/tansell/gob/openpdk2/s8/custom/scs8hd/cdl/scs8hd_clkdlybuf4s18_2.cdl')
('scs8hd', 'clkdlybuf4s18_2')
>>> mod_extract_from_path('/home/tansell/gob/tims-openpdk-repo/s8/libraries/efs8hd/hspice/efs8hd_xor3_2.spi')
('scs8hd', 'xor3_2')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/SKILL/001-59001/s8/s8x/info/s8-generic.il')
('s8x', None)
>>> mod_extract_from_path('/home/tansell/gob/openpdk2/s8/custom/techLEF/scs8hd.lef')
('scs8hd', None)
>>> mod_extract_from_path('/home/tansell/gob/tims-openpdk-repo/s8/libraries/efs8hd/hspice/efab_lib_a2111oi_2.spi')
('scs8hd', 'a2111oi_2')
>>> mod_extract_from_path('/home/tansell/projects/efabless/tech/SW/EFS8A/libs.tech/models/fnpass.pm3')
(None, 'fnpass')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.1/VirtuosoOA/libs/s8rf2/s8rf2_xcmvpp11p5x11p7_m3_lim5shield/extracted/spice.sp')
('s8rf2', 's8rf2_xcmvpp11p5x11p7_m3_lim5shield')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/scs8hd/V0.0.I/hspice/scs8hd_clkdlybuf4s50_1.sp')
('scs8hd', 'clkdlybuf4s50_1')
>>> mod_extract_from_path('/home/tansell/gob/openpdk2/s8/skywater/s8_spice_models/pshort_rf_base_b_wafer.cor')
('s8_spice_models', 'pshort_rf_base_b_wafer')
>>> mod_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/libs/s8phirs_10r/CAP2MM5/symbolic/thumbnail_128x128.png')
('s8phirs_10r', 'cap2mm5')
>>> mod_extract_from_path('/home/jjatczak/Dokumenty/SkyWater/skywater-src-nda/s8iom0s8/V0.2.1/verilog/s8iom0s8_top_tp1.v')
('s8iom0s8', 'top_tp1')
>>> mod_extract_from_path('/home/jjatczak/Dokumenty/SkyWater/skywater-src-nda/s8iom0s8/V0.0.0/oa/s8_esd/s8_esd_gnd2gnd_120x2_lv/behavioral/verilog.v')
('s8_esd', 'gnd2gnd_120x2_lv')
>>> mod_extract_from_path('/home/jjatczak/Dokumenty/SkyWater/skywater-src-nda/s8/V1.3.0/VirtuosoOA/libs/s8phirs_10r_old/bkeep_p/verilog/verilog.v')
('s8phirs_10r_old', 'bkeep_p')
>>> mod_extract_from_path('/ssd/gob/foss-eda-tools/skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/tech/bkeep_p/verilog/verilog.v')
('s8', 'bkeep_p')
>>> mod_extract_from_path('/ssd/gob/foss-eda-tools/skywater-src-nda-openram/s8sram/V0.0.0/cells/sram_cell_1rw_1r/cell_1rw_1r.gds')
('s8sram', 'cell_1rw_1r')
"""
if 'examples' in pn:
return None, None
pn = EFS8_RE.sub('scs8\\1', pn)
bits = pn.split('/')
fname = bits.pop(-1)
fbase, ext = os.path.splitext(fname)
libnames = set()
modnames = set()
file_lib, file_mod = lib_extract_from_name(fbase)
if file_lib:
libnames.add(file_lib)
# Files with corners...
if ext == '.lib':
libname, corner = corners_extract_from_filename(fname)
libnames.add(libname)
if corner.mod:
modnames.add(corner.mod)
# Look for s8 / scs8 type library names
bitresult = None
for b in bits:
m = SCS8_RE.match(b)
if not m:
continue
lib = m.group(0)
if lib.endswith('_'):
bitresult = lib[:-1]
else:
bitresult = lib
if bitresult is not None and len(libnames) == 1:
core = list(libnames)[0]
if core in bitresult:
libnames.pop()
libnames.add(bitresult)
for b in bits:
m = S8_RE.match(b)
if not m:
continue
lib = m.group(0)
if not lib.startswith('s8p'):
if lib.endswith('_'):
libnames.add(lib[:-1])
else:
libnames.add(lib)
for b in bits:
if len(b) <= 2:
continue
if '_' not in b:
if b.startswith('s8'):
libnames.add(b)
if b.startswith('scs8'):
libnames.add(b)
try:
libdir = bits.index('libraries')
if len(bits) > libdir+1:
libnames.add(bits[libdir+1])
if len(bits) > libdir+2 and bits[libdir+2] in PATHS:
libnames.add(bits[libdir+1])
modnames.add(fbase)
except ValueError:
pass
try:
libdir = bits.index('s8spice')
modnames.add(fbase)
except ValueError:
pass
# OpenAccess / Specter directories
if 'VirtuosoOA' not in bits:
if bits[-1] == 'verilog' and ext == '.v' and not fbase == 'verilog':
modnames.add(fbase)
try:
i = bits.index('custom')
if len(bits) > i+1:
libnames.add(bits[i+1])
modnames.add(fbase)
except ValueError:
pass
try:
i = bits.index('VirtuosoOA')
if bits[i+1] == 'libs':
if len(bits) > i+2:
libname = bits[i+2]
if libname in ('technology_library', 'tech'):
libname = 's8'
libnames.add(libname)
if len(bits) > i+3:
modname = bits[i+3]
modnames.add(modname)
except ValueError:
pass
try:
i = bits.index('oa')
if len(bits) > i+1:
libnames.add(bits[i+1])
if len(bits) > i+2:
modnames.add(bits[i+2])
except ValueError:
pass
try:
i = bits.index('SPECTRE')
if len(bits) > i+2:
assert bits[i+2] == "Models", (bits[i+2], bits, pn)
libnames.add(bits[i+1])
modnames.add(fbase)
except ValueError:
pass
# efabless / magic structure
try:
libdir = bits.index('libs.ref')
if len(bits) > libdir+2 and bits[libdir+2] in PATHS:
libnames.add(bits[libdir+1])
modnames.add(fbase)
if len(bits) > libdir+2 and bits[libdir+1] in PATHS:
libnames.add(bits[libdir+2])
modnames.add(fbase)
except ValueError:
pass
try:
libdir = bits.index('libs.tech')
if bits[libdir+1] in PATHS:
modnames.add(fbase)
except ValueError:
pass
try:
libdir = bits.index('s8_spice_models')
libnames.add('s8_spice_models')
modnames.add(fbase)
except ValueError:
pass
modlibnames = list(modnames)
modnames = set()
for modlibname in modlibnames:
libname, modname = lib_extract_from_name(modlibname)
if modname:
modnames.add(cleanup_module_name(modname))
if not modnames and file_mod and "_" in file_mod:
modnames.add(file_mod)
modname = None
if modnames:
assert len(modnames) == 1, (modnames, pn)
modname = modnames.pop()
to_remove = []
for l in libnames:
if not l or '.oa.' in l:
to_remove.append(l)
for l in to_remove:
libnames.remove(l)
_libnames_fixup(libnames)
libname = None
if libnames:
assert len(libnames) == 1, (libnames, pn)
libname = libnames.pop()
libname = libname.replace('-rechar', '')
return libname, modname
def lib_extract_from_path(pn):
"""
>>> lib_extract_from_path('/home/tansell/gob/tims-openpdk-repo/s8/libraries/efs8_pads/lef/efs8_pads.lef')
'efs8_pads'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/lef/s8iom0s8.lef')
's8iom0s8'
>>> lib_extract_from_path('/home/tansell/gob/ef-skywater-s8/EFS8A/libs.ref/lef/primdev/primdev.lef')
'primdev'
>>> lib_extract_from_path('/home/tansell/gob/tims-openpdk-repo/s8/libraries/s8_osu130/lef/s8_osu130.lef')
's8_osu130'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/MODELS/SPECTRE/s8x/Models/xcmvpp_ponly.mod')
's8x'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.1/VirtuosoOA/libs/s8rf2/s8rf2_xcmvpp11p5x11p7_m3_lim5shield/extracted/spice.sp')
's8rf2'
>>> lib_extract_from_path('/home/tansell/gob/openpdk2/s8/skywater/s8_spice_models/xcmvpp_all_cu.mod')
's8_spice_models'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/scs8hs/V0.0.0/lib/scs8hs_ff_1.56v_-40C.lib')
'scs8hs'
>>> lib_extract_from_path('/home/tansell/gob/ef2/matt/scs8hd_tt_1.80v_25C_rechar.lib')
'scs8hd'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8/V1.0.0/VirtuosoOA/libs/s8phirs_10r/pfetextd/spectre/data.dm')
's8phirs_10r'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8io_m0s8/.oalib')
's8iom0s8'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/lib/s8iom0s8_top_xres4v2_ff_ss_1p95v_x_1p95v_100C.lib')
's8iom0s8'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8_esd/s8_esd_Li_40K_2_RevA_south/layout/master.tag')
's8_esd'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8_esd/s8_esd_gnd2gnd_120x2_lv_isosub/symbol/symbol.oa')
's8_esd'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8iom0s8/s8iom0s8_amx_xor/schematic/data.dm')
's8iom0s8'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/s8iom0s8/V0.0.0/oa/s8iom0s8_dv/s8rf_xcmvpp8p6x7p9_m3shield/layout/layout.oa')
's8rf'
>>> lib_extract_from_path('/home/tansell/github/google/skywater-pdk/scs8hd/V0.0.1/oa/scs8_hd/.oalib')
'scs8hd'
>>> lib_extract_from_path('/home/tansell/projects/efabless/tech/SW/EFS8A/libs.ref/efs8_pads/mag/efs8_pads.mag')
'efs8_pads'
>>> lib_extract_from_path('/home/tansell/projects/efabless/tech/SW/EFS8A/libs.ref/efs8_pads/mag/analog200ohm_pad.mag')
'efs8_pads'
>>> lib_extract_from_path('/ssd/gob/foss-eda-tools/skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/tech/bkeep_p/verilog/verilog.v')
's8'
>>> lib_extract_from_path('/ssd/gob/foss-eda-tools/skywater-src-nda-openram/s8sram/V0.0.0/cells/sram_cell_1rw_1r/cell_1rw_1r.gds')
's8sram'
"""
libname, modname = mod_extract_from_path(pn)
return libname
COMMENTS = {
'c': (['//'], [('/*','*/')]),
'spice': (['//', '\n*'], [('/*','*/')]),
'shell': (['#'], []),
}
def read_without_cmts(file, comments, debug=None):
""""
>>> s = '''\
... no comment
... end comment // here
... // full line end comment
... /* full line multiline comment */
... before /* line one
... line two
... line three */ after
... before /* inside */ after
... a/*b*/c/*d*/
... /*a*/b/*c*//*d*/
... /*a*//*b*//*c*/d/*e*/
... '''
>>> for l in read_without_cmts(io.StringIO(s), 'c'):
... print(repr(l))
' ... no comment'
'end comment '
'before after'
'before after'
'ac'
'b'
'd'
>>> for l in read_without_cmts(io.StringIO(s), [['/*'], []]):
... print(repr(l))
' ... no comment'
'end comment // here'
'// full line end comment'
'before '
' line two'
' line three */ after'
'before '
'a'
>>> for l in read_without_cmts(io.StringIO(s), [[], [('/','/')]]):
... print(repr(l))
' ... no comment'
'end comment here'
' full line end comment'
'before after'
'before after'
'ac'
'b'
'd'
"""
if isinstance(comments, str):
cmt_lines, cmt_multilines = COMMENTS[comments]
else:
cmt_lines, cmt_multilines = comments
if not debug:
debug = global_debug
if not hasattr(file, 'read'):
file = open(file)
data = file.read()
if cmt_multilines:
re_multiline = []
for start, end in cmt_multilines:
# ..SSxxEE..
re_multiline.append('({}(.*?){})'.format(re.escape(start), re.escape(end)))
data = re.sub('|'.join(re_multiline), '', data, flags=re.DOTALL)
for l in data.splitlines():
# Remove single line comments
# ^..SSxxx$
for cmt_line in cmt_lines:
cmt_start_pos = l.find(cmt_line)
if cmt_start_pos > -1:
debug("cmt", cmt_line, repr(l[cmt_start_pos:]))
l = l[:cmt_start_pos]
if l:
debug(' >', repr(l))
yield l
def read_cmts(file, comments, debug=None, include_delimeters=False):
""""
>>> s = '''\
... no comment
... end comment // here1
... end comment // here2
... // full line end comment1
... // full line end comment2
... /* full line multiline comment */
... before /* line one
... line two
... line three */ after
... before /* inside */ after
... a/*b*/c/*d*/
... /*a*/b/*c*//*d*/
... /*a*//*b*//*c*/d/*e*/
... /* a1
... * a2
... * a3
... * a4
... */
... /* b1
... * b2
... * b3
... * b4
... */
... /* list intro
... * list 1
... * list 2
... */
... '''
>>> for l in read_cmts(io.StringIO(s), 'c', include_delimeters=True):
... print(repr(l))
'// here1\\n// here2\\n'
'// full line end comment1\\n// full line end comment2\\n'
'/* full line multiline comment */'
'/* line one\\n line two\\n line three */'
'/* inside */'
'/*b*/'
'/*d*/'
'/*a*/'
'/*c*/'
'/*d*/'
'/*a*/'
'/*b*/'
'/*c*/'
'/*e*/'
'/* a1\\n * a2\\n * a3\\n * a4\\n */'
'/* b1\\n * b2\\n * b3\\n * b4\\n */'
'/* list intro\\n * list 1\\n * list 2\\n */'
>>> for l in read_cmts(io.StringIO(s), 'c', include_delimeters=False):
... print(repr(l))
'here1\\nhere2\\n'
'full line end comment1\\nfull line end comment2\\n'
'full line multiline comment'
'line one\\nline two\\nline three'
'inside'
'b'
'd'
'a'
'c'
'd'
'a'
'b'
'c'
'e'
'a1\\na2\\na3\\na4\\n'
'b1\\nb2\\nb3\\nb4\\n'
'list intro\\n* list 1\\n* list 2\\n'
"""
if isinstance(comments, str):
cmt_lines, cmt_multilines = COMMENTS[comments]
else:
cmt_lines, cmt_multilines = comments
if not debug:
debug = global_debug
if not hasattr(file, 'read'):
file = open(file)
data = file.read()
cmt_delimiters = []
for cmt_start in cmt_lines:
cmt_delimiters.append((cmt_start, '\n'))
cmt_delimiters.extend(cmt_multilines)
unprocessed = data
in_comment = False
comments_pending = []
comments_pending_type = ''
while unprocessed:
next_start_pos = {}
for cmt_start, cmt_end in cmt_delimiters:
start_pos = unprocessed.find(cmt_start)
if start_pos > -1:
assert start_pos not in next_start_pos, (start_pos, next_start_pos)
next_start_pos[start_pos] = (cmt_start, cmt_end)
debug(' cmt', next_start_pos)
if not next_start_pos:
next_start_pos[len(unprocessed)] = ('', '')
min_start_pos = min(next_start_pos.keys())
cmt_start, cmt_end = next_start_pos[min_start_pos]
non_comment, unprocessed = unprocessed[:min_start_pos], unprocessed[min_start_pos:]
if non_comment:
lastpos = non_comment.rfind('\n')
if lastpos > -1:
lastline = non_comment[lastpos+1:]
else:
lastline = non_comment
comments_current_type = 'X'*len(lastline)+cmt_start
else:
comments_current_type = cmt_start
if comments_pending and (cmt_end != '\n' or comments_current_type != comments_pending_type):
# Flush out any pending comments
assert comments_pending, (comments_pending, comments_pending_type, comments_current_type)
if not include_delimeters:
lines = ''.join(comments_pending).splitlines(True)
# FIXME: This is a hack....
rpos = comments_pending_type.rfind('X')
if len(comments_pending_type) > (rpos+2) and comments_pending_type[rpos+2] == '*':
rpos += 2
prefix = ' '*rpos+'*'
for i in range(1, len(lines)):
if lines[i].startswith(prefix):
lines[i] = lines[i][len(prefix):]
comment = lines[0].lstrip()
comment += textwrap.dedent(''.join(lines[1:]))
comment = comment.strip(' \t')
else:
comment = ''.join(comments_pending)
debug(' cmt', repr(comment))
yield comment
comments_pending.clear()
if non_comment:
for l in non_comment.split('\n'):
debug(' >', repr(l))
if not unprocessed:
break
next_end_pos_a = unprocessed.find(cmt_end)
if next_end_pos_a < 0:
debug(' !!!', 'Missing %r for closing %r' % (cmt_end, cmt_start))
next_end_pos_a = len(unprocessed)
next_end_pos_b = next_end_pos_a+len(cmt_end)
next_start_pos_a = len(cmt_start)
if include_delimeters:
next_start_pos_a = 0
next_end_pos_a += len(cmt_end)
elif cmt_end == '\n':
next_end_pos_a += 1
comment, unprocessed = unprocessed[next_start_pos_a:next_end_pos_a], unprocessed[next_end_pos_b:]
comments_pending.append(comment)
comments_pending_type = comments_current_type
def convert_libname(l):
"""
>>> # Library Names table
>>> convert_libname('s8phirs_10r')
'sky130_fd_pr'
>>> convert_libname('s8x')
'sky130_fd_pr'
>>> convert_libname('s8rf')
'sky130_fd_pr'
>>> convert_libname('s8rf2')
'sky130_fd_pr'
>>> convert_libname('scs8hd')
'sky130_fd_sc_hd'
>>> convert_libname('scs8hdll')
'sky130_fd_sc_hdll'
>>> convert_libname('scs8hs')
'sky130_fd_sc_hs'
>>> convert_libname('scs8ms')
'sky130_fd_sc_ms'
>>> convert_libname('scs8ls')
'sky130_fd_sc_ls'
>>> convert_libname('scs8lp')
'sky130_fd_sc_lp'
>>> convert_libname('scs8lpa')
'sky130_fd_sc_lp'
>>> convert_libname('s8iom0s8')
'sky130_fd_io'
>>> convert_libname('efs8_pads')
'sky130_ef_io'
>>> convert_libname('osu130')
'sky130_osu_sc'
>>> convert_libname('s8_esd')
'sky130_fd_io'
>>> convert_libname('s8')
'sky130_fd_pr'
>>> convert_libname('s8_spice_models')
'sky130_fd_pr'
>>> convert_libname('s8sram')
'sky130_fd_bd_sram'
"""
l = l.replace('scs8lpa', 'scs8lp')
if l == None:
return ""
if l in ('s8sram','s8_dp_sram'):
return 'sky130_fd_bd_sram'
assert 'sram' not in l, l
if l in ('s8phirs_10r', 's8', 's8x'):
return 'sky130_fd_pr'
if l in 's8_spice_models':
return 'sky130_fd_pr'
if l in ('s8rf', 's8rf2') :
return 'sky130_fd_pr'
if l == 's8phirs_10r_old':
return 'sky130_fd_pr_old'
if l == 'efs8_pads':
return 'sky130_ef_io'
if l == 'osu130':
return 'sky130_osu_sc'
if l == 's8_osu130':
return 'sky130_osu'
if l == 's8iom0s8':
return 'sky130_fd_io'
if l == 's8_esd':
return 'sky130_fd_io'
if 'scs8' in l:
assert '_' not in l, l
return l.replace('scs8', 'sky130_fd_sc_')
if 's8' in l:
assert '_' not in l, l
return l.replace('s8', 'sky130_fd_')
STRENGTH_RE = re.compile(r'_([0-9]|[1-9][0-9]|lp|lp2|m)$')
def strip_strength(s):
"""
>>> strip_strength('XXXX_0')
'XXXX'
>>> strip_strength('XXXX_12')
'XXXX'
>>> strip_strength('XXXX_1')
'XXXX'
>>> strip_strength('XXXX_lp')
'XXXX'
>>> strip_strength('XXXX_lp2')
'XXXX'
>>> strip_strength('XXXX_m')
'XXXX'
>>> strip_strength('XXXX_ABC')
'XXXX_ABC'
>>> strip_strength('XXXX_12m')
'XXXX_12m'
>>> strip_strength('XXXX_02')
'XXXX_02'
>>> strip_strength('a41oi_4')
'a41oi'
"""
return STRENGTH_RE.sub('',s)
def patch_string(s, replacements):
if s in replacements:
return True, replacements[s]
for from_str, to_str in replacements.items():
new_str = s.replace(from_str, to_str)
if s != new_str:
return True, new_str
return False, s
def directory_for_cell(cell_fullname):
"""
>>> directory_for_cell('sky130_fd_pr__rf_pfet_01v8_aF02W0p84L0p15__tt')
('cells', 'rf_pfet_01v8')
>>> directory_for_cell('sky130_fd_io__top_filter_narrow')
('cells', 'top_filter_narrow')
>>> directory_for_cell('sky130_fd_io__top_filter_narrow_1')
('cells', 'top_filter_narrow')
>>> directory_for_cell('sky130_fd_sc_hd__udp_dlatch$P')
('models', 'udp_dlatch_p')
>>> directory_for_cell('sky130_fd_sc_hd__mux2i_0')
('cells', 'mux2i')
>>> directory_for_cell('sky130_fd_pr__l1m1_pr__example1')
('cells', 'l1m1_pr')
>>> directory_for_cell('sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p15')
('cells', 'rf_nfet_01v8_lvt')
>>> directory_for_cell('sky130_fd_pr__model__diode_connection')
('models', '')
>>> directory_for_cell(convert_cell_fullname('nor2_p'))
('models', 'typical')
>>> directory_for_cell(convert_cell_fullname('xcmvpp_moscap'))
('models', 'capacitors')
>>> directory_for_cell(convert_cell_fullname('nwdiode'))
('models', 'parasitics')
>>> directory_for_cell(convert_cell_fullname('dnwdiode_pw'))
('models', 'parasitics')
"""
assert cell_fullname.startswith('sky130'), cell_fullname
assert '__' in cell_fullname, cell_fullname
lib_name, cell_name = cell_fullname.split('__', 1)
d0 = 'cells'
if cell_name.startswith('udp'):
d0 = 'models'
d1 = cell_name.replace('$', '_').lower()
elif cell_name.startswith('model__'):
d0 = "models"
d1 = cell_name[len('model__'):]
else:
d1 = strip_strength(cell_name)
if '__example' in d1:
d1 = d1.split('__example')[0]
if cell_fullname in MAPPING_CELLNAME2DIR:
d1 = MAPPING_CELLNAME2DIR[cell_fullname]
elif cell_fullname.count('__') > 1:
cell_fullname, corners = cell_fullname.rsplit('__', 1)
if cell_fullname in MAPPING_CELLNAME2DIR:
d1 = MAPPING_CELLNAME2DIR[cell_fullname]
return (d0, d1)
def get_cell_directory(output_dir, libname, version, cellname):
"""
>>> get_cell_directory('output', 'sky130_fd_io', 'v0.0.1', 'sky130_fd_io__top_filter_narrow')
'output/skywater-pdk/libraries/sky130_fd_io/v0.0.1/cells/top_filter_narrow'
>>> get_cell_directory('output', 'sky130_fd_io', 'v0.0.1', 'sky130_fd_io__top_filter_narrow_1')
'output/skywater-pdk/libraries/sky130_fd_io/v0.0.1/cells/top_filter_narrow'
>>> get_cell_directory('output', 'sky130_fd_io', 'v0.0.1', 'sky130_fd_io__a2bb2oi_1')
'output/skywater-pdk/libraries/sky130_fd_io/v0.0.1/cells/a2bb2oi'
# ./libraries/sky130_fd_sc_lp/V0.0.0/cells/a211oi_lp/sky130_fd_sc_lp__a211oi_lp.gds
>>> get_cell_directory('output', 'sky130_fd_sc_lp', 'v0.0.1', 'sky130_fd_sc_lp__a211oi_lp')
'output/skywater-pdk/libraries/sky130_fd_sc_lp/v0.0.1/cells/a211oi'
# ./libraries/sky130_fd_sc_lp/V0.0.0/cells/a211oi_lp2/sky130_fd_sc_lp__a211oi_lp2.gds
>>> get_cell_directory('output', 'sky130_fd_sc_lp', 'v0.0.1', 'sky130_fd_sc_lp__a211oi_lp2')
'output/skywater-pdk/libraries/sky130_fd_sc_lp/v0.0.1/cells/a211oi'
# ./libraries/sky130_fd_sc_lp/V0.0.0/cells/a211oi_m/sky130_fd_sc_lp__a211oi_m.gds
>>> get_cell_directory('output', 'sky130_fd_sc_lp', 'v0.0.1', 'sky130_fd_sc_lp__a211oi_m')
'output/skywater-pdk/libraries/sky130_fd_sc_lp/v0.0.1/cells/a211oi'
>>> get_cell_directory('output', 'sky130_fd_sc_lp', 'v0.0.1', 'sky130_fd_sc_hd__udp_dlatch$P')
'output/skywater-pdk/libraries/sky130_fd_sc_lp/v0.0.1/models/udp_dlatch_p'
>>> get_cell_directory('output', 'sky130_fd_sc_lp', 'v0.0.1', 'liberty')
'output/skywater-pdk/libraries/sky130_fd_sc_lp/v0.0.1/timing'
>>> get_cell_directory('output', 'sky130_fd_sc_lp', 'v0.0.1', 'techlef')
'output/skywater-pdk/libraries/sky130_fd_sc_lp/v0.0.1/tech'
>>> get_cell_directory('output', 'sky130_fd_sc_lp', 'v0.0.1', 'common')
'output/skywater-pdk/libraries/sky130_fd_sc_lp/v0.0.1'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', 'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4_top.gds')
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/cells/cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', 'sky130_fd_pr__esd_pfet_g5v0d10v5__tt_leak.cor')
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/cells/esd_pfet_g5v0d10v5'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', 'sky130_fd_pr__esd_pfet_g5v0d10v5__tt_leak')
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/cells/esd_pfet_g5v0d10v5'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', 'sky130_fd_pr__l1m1_pr__example1')
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/cells/l1m1_pr'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', 'sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p15')
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/cells/rf_nfet_01v8_lvt'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', convert_cell_fullname('nwdiode'))
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/models/parasitics'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', convert_cell_fullname('dnwdiode_pw'))
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/models/parasitics'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', convert_cell_fullname('dnwdiode_psub_n20nativevhv1'))
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/cells/nfet_20v0_nvt'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', convert_cell_fullname('nor2_p'))
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/models/typical'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', convert_cell_fullname('xcmvpp_moscap'))
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/models/capacitors'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', convert_cell_fullname('condiode'))
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/models'
>>> get_cell_directory('output', 'sky130_fd_pr', 'v0.0.1', 'sky130_fd_pr__rf_pfet_01v8_aF02W0p84L0p15__tt')
'output/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/cells/rf_pfet_01v8'
"""
assert version.startswith('v'), version
if 'common' == cellname:
return f"{output_dir}/skywater-pdk/libraries/{libname}/{version}"
elif 'liberty' == cellname:
return f"{output_dir}/skywater-pdk/libraries/{libname}/{version}/timing"
elif 'techlef' == cellname:
return f"{output_dir}/skywater-pdk/libraries/{libname}/{version}/tech"
cellname_base = cellname
if '.' in cellname:
cellname_base, ext = cellname.split('.', 1)
dirs = directory_for_cell(cellname_base)
return f"{output_dir}/skywater-pdk/libraries/{libname}/{version}/{dirs[0]}/{dirs[1]}".rstrip('/')
def get_random_string(length):
return ''.join(random.choice(string.digits) for i in range(length))
def decoder_convert_cellname(old):
"""
>>> decoder_convert_cellname('hrpoly_0p35$$175320108')
'sky130_fd_pr__res_high_po_0p35__example1'
#>>> s = get_random_string(len('17532010'))
#>>> decoder_convert_cellname('hrpoly_0p88$$'+s).rsplit('_', 1)[0]
#'sky130_fd_pr__res_high_po_0p88__example'
>>> decoder_convert_cellname("condiode")
'sky130_fd_pr__model__diode_connection'
>>> decoder_convert_cellname('dnwdiode_psub')
'sky130_fd_pr__model__parasitic__diode_ps2dn'
>>> decoder_convert_cellname('vpp_nhvnative10x4')
'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhv__base'
>>> decoder_convert_cellname('xcmvppx4_2xnhvnative10x4_noextrafingers_raphael')
'sky130_fd_pr__cap_vpp_11p3x11p8_m1m2_noshield_nhv2raphael'
>>> decoder_convert_cellname('xcmvppx4_2xnhvnative10x4_raphael')
'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhvraphael'
>>> decoder_convert_cellname('xcmvppx4_2xnhvnative10x4_top')
'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhvtop'
>>> decoder_convert_cellname('ind_03')
'sky130_fd_pr__ind_03'
>>> decoder_convert_cellname('ind_05')
'sky130_fd_pr__ind_05'
>>> decoder_convert_cellname('xind4_011')
'sky130_fd_pr__ind_11_04'
>>> decoder_convert_cellname('xind4_02')
'sky130_fd_pr__ind_02_04'
>>> decoder_convert_cellname('rf_xind4_01')
'sky130_fd_pr__ind_01_04'
>>> decoder_convert_cellname('s8blref_xind4_01')
'sky130_fd_pr__ind_01_04'
>>> decoder_convert_cellname('ntran')
'sky130_fd_pr__model__nfet'
>>> decoder_convert_cellname('extdntran')
'sky130_fd_pr__model__nfet_extendeddrain'
>>> decoder_convert_cellname('fuse_m3m4')
'sky130_fd_pr__fuse_m3m4'
>>> decoder_convert_cellname('m4fuse')
'sky130_fd_pr__fuse_m4'
>>> decoder_convert_cellname('condiode')
'sky130_fd_pr__model__diode_connection'
>>> decoder_convert_cellname('condiodehvpsub')
'sky130_fd_pr__model__parasitic__diode_ps2dn_highvoltage'
>>> decoder_convert_cellname('nwdiode_no_rs')
'sky130_fd_pr__model__parasitic__diode_ps2nw_noresistor'
#>>> decoder_convert_cellname('hvDFL1sd')
''
#>>> decoder_convert_cellname('hvDFL1sd2')
''
"""
#old = old.lower()
#if old in MAPPING_OLD2NEW_CELLNAME:
# newname = MAPPING_OLD2NEW_CELLNAME[old]
# return newname
try:
cellinfo, s = decoder.parse_name(old)
if s:
return None
if not cellinfo:
return None
cell_basename, cell_fullname = decoder.newname(cellinfo)
if not cell_fullname:
return None
MAPPING_OLD2NEW_CELLNAME[old] = cell_fullname
MAPPING_CELLNAME2DIR[cell_fullname] = cell_basename
return cell_fullname
except Exception as e:
sys.stderr.write('Decoder error with {}: {}\n'.format(old, e))
return None
RE_EXAMPLE = re.compile('(?:_|(?:[$][$]))([0-9][0-9][0-9][0-9]+)$')
SRAM_CELLS = {
'sram_dff' : 'openram_sram_dff',
'nand2_dec' : 'openram_nand2_dec',
'nand3_dec' : 'openram_nand3_dec',
'nand4_dec' : 'openram_nand4_dec',
'sense_amp' : 'openram_sense_amp',
'write_driver' : 'openram_write_driver',
'cell_1rw_1r' : 'openram_cell_1rw_1r',
'col_cap_cell_1rw_1r' : 'openram_cell_1rw_1r_cap_col',
'dummy_cell_1rw_1r' : 'openram_cell_1rw_1r_dummy',
'replica_cell_1rw_1r' : 'openram_cell_1rw_1r_replica',
'row_cap_cell_1rw_1r' : 'openram_cell_1rw_1r_cap_row',
'cell_6t' : 'openram_cell_6t',
'col_cap_cell_6t' : 'openram_cell_6t_cap_col',
'dummy_cell_6t' : 'openram_cell_6t_dummy',
'replica_cell_6t' : 'openram_cell_6t_replica',
'row_cap_cell_6t' : 'openram_cell_6t_cap_row',
#'cell_1rw_1r' : 'openram_cell_1rw_1r',
'cell_1rw_1r_col_cap' : 'openram_cell_1rw_1r_cap_col',
'cell_1rw_1r_dummy' : 'openram_cell_1rw_1r_dummy',
'cell_1rw_1r_replica' : 'openram_cell_1rw_1r_replica',
'cell_1rw_1r_row_cap' : 'openram_cell_1rw_1r_cap_row',
#'cell_6t' : 'openram_cell_6t',
'cell_6t_col_cap' : 'openram_cell_6t_cap_col',
'cell_6t_dummy' : 'openram_cell_6t_dummy',
'cell_6t_replica' : 'openram_cell_6t_replica',
'cell_6t_row_cap' : 'openram_cell_6t_cap_row',
'L1M1_CDNS_594327665970': 'sram_l1m1',
'l1m1_cdns_594327665970': 'sram_l1m1',
}
for a, b in list(SRAM_CELLS.items()):
SRAM_CELLS['sram_'+a] = b
for a, b in list(SRAM_CELLS.items()):
SRAM_CELLS['openram_'+a] = b
SRAM_CELLS['openram_openram_'+a] = b
def convert_cellname(old_cellname, new_libname=None):
"""
>>> convert_cellname("xxxxx")
'xxxxx'
>>> convert_cellname("XXXXX")
'xxxxx'
>>> convert_cellname("m1m2_pr_cdns")
'm1m2_pr'
>>> convert_cellname("s8blref_xind4_01")
'ind_01_04'
>>> convert_cellname("L1M1_PR_CDNS_525975574200")
'l1m1_pr__example_525975574200'
>>> convert_cellname("s8pir_10r_vcells_lvs")
'sky130_vcells_lvs'
>>> convert_cellname('S8BLAH')
'blah'
>>> convert_cellname('s8rf_pshort_W3p0_L0p15_2F')
'rf_pfet_01v8_aF02W3p00L0p15'
SRAM cells
>>> convert_cellname('s8Dp_blkinv_opt1')
'sram_dp_blkinv_opt1'
>>> convert_cellname('s8sram16x16_colend_p_cent')
'sram_sp_colend_p_cent'
>>> convert_cellname('s8sram_colend_cent')
'sram_sp_colend_cent'
No prefix
---------
>>> convert_cellname('sram_dff')
'openram_sram_dff'
>>> convert_cellname('nand2_dec')
'openram_nand2_dec'
>>> convert_cellname('nand3_dec')
'openram_nand3_dec'
>>> convert_cellname('nand4_dec')
'openram_nand4_dec'
>>> convert_cellname('sense_amp')
'openram_sense_amp'
>>> convert_cellname('write_driver')
'openram_write_driver'
>>> convert_cellname('cell_1rw_1r')
'openram_cell_1rw_1r'
>>> convert_cellname('col_cap_cell_1rw_1r')
'openram_cell_1rw_1r_cap_col'
>>> convert_cellname('dummy_cell_1rw_1r')
'openram_cell_1rw_1r_dummy'
>>> convert_cellname('replica_cell_1rw_1r')
'openram_cell_1rw_1r_replica'
>>> convert_cellname('row_cap_cell_1rw_1r')
'openram_cell_1rw_1r_cap_row'
>>> convert_cellname('cell_6t')
'openram_cell_6t'
>>> convert_cellname('col_cap_cell_6t')
'openram_cell_6t_cap_col'
>>> convert_cellname('dummy_cell_6t')
'openram_cell_6t_dummy'
>>> convert_cellname('replica_cell_6t')
'openram_cell_6t_replica'
>>> convert_cellname('row_cap_cell_6t')
'openram_cell_6t_cap_row'
OpenRAM prefix
--------------
>>> convert_cellname('openram_dff')
'openram_dff'
>>> convert_cellname('openram_nand2_dec')
'openram_nand2_dec'
>>> convert_cellname('openram_nand3_dec')
'openram_nand3_dec'
>>> convert_cellname('openram_nand4_dec')
'openram_nand4_dec'
>>> convert_cellname('openram_sense_amp')
'openram_sense_amp'
>>> convert_cellname('openram_write_driver')
'openram_write_driver'
>>> convert_cellname('openram_cell_1rw_1r')
'openram_cell_1rw_1r'
>>> convert_cellname('openram_col_cap_cell_1rw_1r')
'openram_cell_1rw_1r_cap_col'
>>> convert_cellname('openram_dummy_cell_1rw_1r')
'openram_cell_1rw_1r_dummy'
>>> convert_cellname('openram_replica_cell_1rw_1r')
'openram_cell_1rw_1r_replica'
>>> convert_cellname('openram_row_cap_cell_1rw_1r')
'openram_cell_1rw_1r_cap_row'
>>> convert_cellname('openram_cell_6t')
'openram_cell_6t'
>>> convert_cellname('openram_col_cap_cell_6t')
'openram_cell_6t_cap_col'
>>> convert_cellname('openram_dummy_cell_6t')
'openram_cell_6t_dummy'
>>> convert_cellname('openram_replica_cell_6t')
'openram_cell_6t_replica'
>>> convert_cellname('openram_row_cap_cell_6t')
'openram_cell_6t_cap_row'
SRAM prefix
-----------
>>> convert_cellname('sram_dff')
'openram_sram_dff'
>>> convert_cellname('sram_nand2_dec')
'openram_nand2_dec'
>>> convert_cellname('sram_nand3_dec')
'openram_nand3_dec'
>>> convert_cellname('sram_nand4_dec')
'openram_nand4_dec'
>>> convert_cellname('sram_sense_amp')
'openram_sense_amp'
>>> convert_cellname('sram_write_driver')
'openram_write_driver'
>>> convert_cellname('sram_cell_1rw_1r')
'openram_cell_1rw_1r'
>>> convert_cellname('sram_col_cap_cell_1rw_1r')
'openram_cell_1rw_1r_cap_col'
>>> convert_cellname('sram_dummy_cell_1rw_1r')
'openram_cell_1rw_1r_dummy'
>>> convert_cellname('sram_replica_cell_1rw_1r')
'openram_cell_1rw_1r_replica'
>>> convert_cellname('sram_row_cap_cell_1rw_1r')
'openram_cell_1rw_1r_cap_row'
>>> convert_cellname('sram_cell_6t')
'openram_cell_6t'
>>> convert_cellname('sram_col_cap_cell_6t')
'openram_cell_6t_cap_col'
>>> convert_cellname('sram_dummy_cell_6t')
'openram_cell_6t_dummy'
>>> convert_cellname('sram_replica_cell_6t')
'openram_cell_6t_replica'
>>> convert_cellname('sram_row_cap_cell_6t')
'openram_cell_6t_cap_row'
>>> convert_cellname('libcell')
'libcell'
>>> convert_cellname('libcell_tap')
'tap'
>>> convert_cellname('latchupcell')
'latchupcell'
>>> convert_cellname('hrpoly_0p35_l1m1con_175323180')
'res_high_pol1m1_0p35__example1'
>>> convert_cellname('hrpoly_0p35_175320108')
'res_high_po_0p35__example1'
>>> convert_cellname('hrpoly_0p35$$175320108')
'res_high_po_0p35__example1'
>>> convert_cellname('nhv_rf_base_b_sf')
'rf_nfet_g5v0d10v5_b__sf'
>>> convert_cellname('nlvtpass_ff')
'special_nfet_pass_lvt__ff'
>>> convert_cellname('nlvtpass_ff_discrete')
'special_nfet_pass_lvt__ff_discrete'
>>> convert_cellname('xcmvpp2_phv5x4')
'cap_vpp_04p4x04p6_m1m2_noshield_o1phv'
>>> convert_cellname('rf2_xcmvpp_hd5_atlas_fingercap2_l5')
'cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_fingercap2'
>>> convert_cellname('dnwdiode_psub')
'model__parasitic__diode_ps2dn'
>>> convert_cellname('xcmvppx4_2xnhvnative10x4_noextrafingers_raphael')
'cap_vpp_11p3x11p8_m1m2_noshield_nhv2raphael'
>>> convert_cellname('xcmvppx4_2xnhvnative10x4_raphael')
'cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhvraphael'
>>> convert_cellname('xcmvppx4_2xnhvnative10x4_top')
'cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhvtop'
>>> convert_cellname('ind_03')
'ind_03'
>>> convert_cellname('ind_05')
'ind_05'
>>> convert_cellname('xind4_011')
'ind_11_04'
>>> convert_cellname('xind4_02')
'ind_02_04'
>>> convert_cellname('rf_xind4_01')
'ind_01_04'
>>> convert_cellname('ntran')
'model__nfet'
>>> convert_cellname('extdntran')
'model__nfet_extendeddrain'
>>> convert_cellname('xcmvpp_moscap')
'model__cap_vpp_only_mos'
>>> convert_cellname('xcmvpp1_subcell')
'cap_vpp_08p6x07p8_l1m1m2_noshield_o1subcell'
>>> convert_cellname('xcmvpp_2')
'cap_vpp_04p4x04p6_l1m1m2_noshield_o1'
>>> convert_cellname('xcnwvc_top')
'model__cap_var'
>>> convert_cellname('pnp')
'pnp_05v5_W0p68L0p68'
>>> convert_cellname('npn')
'npn_05v5_all'
>>> convert_cellname('pnppar')
'pnp_05v5_W0p68L0p68'
>>> convert_cellname('xcmvpp')
'cap_vpp_08p6x07p8_l1m1m2_noshield_o1'
>>> convert_cellname('xcmvpp_top')
'model__cap_vpp'
>>> convert_cellname('npnpar1x1')
'npn_05v5_W1p00L1p00'
"""
old_cellname = re.sub('S8PIR[_-]10R', 'sky130', old_cellname, flags=re.I)
old_cellname = old_cellname.lower()
if old_cellname in SRAM_CELLS:
return SRAM_CELLS[old_cellname]
sram_cell = None
if 's8dp_' in old_cellname:
sram_cell = old_cellname.replace('s8dp_', 'sram_dp_')
elif 's8sram16x16_' in old_cellname:
sram_cell = old_cellname.replace('s8sram16x16_', 'sram_sp_')
elif 's8sram_' in old_cellname:
sram_cell = old_cellname.replace('s8sram_', 'sram_sp_')
elif 'sram_' in old_cellname:
sram_cell = old_cellname.replace('sram_', 'openram_')
elif 'cell_' in old_cellname and not old_cellname.startswith('libcell') and not 'latchupcell' in old_cellname:
sram_cell = 'openram_'+old_cellname
if sram_cell:
if sram_cell in SRAM_CELLS:
return SRAM_CELLS[sram_cell]
else:
return sram_cell
if new_libname is None or '_sc_' not in new_libname:
decoder_newname = decoder_convert_cellname(old_cellname)
if decoder_newname:
assert '__' in decoder_newname, decoder_newname
return decoder_newname.split('__', 1)[1]
old_cellname = old_cellname.replace('libcell_', '')
old_cellname = old_cellname.replace('$$', '_')
old_cellname = old_cellname.replace('s8blerf_', 's8rf_')
old_cellname = old_cellname.replace('s8blref_', 's8rf_')
old_cellname = old_cellname.replace('s8', '')
old_cellname = old_cellname.replace('_cdns', '')
old_cellname = old_cellname.replace('cdns', '')
old_cellname = RE_EXAMPLE.sub('__example_\\1', old_cellname)
corners = []
old_cellname_split = old_cellname.split('_')
while old_cellname_split and old_cellname_split[-1] in CORNERS:
corners.append(old_cellname_split.pop(-1))
old_cellname = "_".join(old_cellname_split)
if corners:
old_cellname += "__" + "_".join(reversed(corners))
return old_cellname
'''
def convert_corner(text, ignoredcorners=None):
"""
>>> convert_corner('ss_xx_1p28lv_hv_io_n40c_hrlc_tpl_PVT1VASTA20')
's_1p28V_lv_n40C'
>>> convert_corner('ss_1.65v_lowhv_1.65v_lv_1.6v_-40C_ccsnoise')
>>> convert_corner('ff_5.5v_-40C')
>>> convert_corner('ff_1.95v_-40C_ccsnoise')
>>> convert_corner('scs8hvl_ff_5.5v_lowhv_1.65v_lv_ss_1.6v_-40C.lib')
"""
if text.endswith('.lib'):
text = text[:-4]
if ignoredcorners is None:
ignoredcorners = set()
text = text.replace('ff_ff', 'ffff')
text = text.replace('ff_ss', 'ffss')
text = text.replace('ss_ff', 'ssff')
text = text.replace('ss_ss', 'ssss')
text = text.replace('ff_xx', 'ffxx')
text = text.replace('tt_xx', 'ttxx')
text = text.replace('ss_xx', 'ssxx')
text = text.replace('hv_io', 'hvio')
oldcorners = [corner for corner in text.split('_') if corner != '']
newcorners = []
prev = ''
prevc = ''
transtab = str.maketrans('-.', 'np')
invtranstab = str.maketrans('np', '-.')
# 'lv' HACK for invalid file format: scs8hdll/V0.1.0/lib/scs8hdll_ff_xx_1p56lv_hv_io_n40c_lrhc_tpl_PVT2VASTA20.lib
VOLTS_RE = re.compile('(?P<core>([\-pn+])?([0-9]+[.p][0-9]+))(?P<islv>[vV]|lv)')
TEMP_RE = re.compile('(?P<sign>([\-pn+])?)(?P<value>[0-9]+)[Cc]')
types = [
'f', # Fast
'fx', # Fast, ???
'ffxx',
's', # Slow
'sx', # Slow, ???
'ssxx',
't', # Typical
'tx', # Slow, ???
'ttxx',
'ff', # Fast Fast
'ffff',
'fs', # Fast Slow
'ffss',
'sf', # Slow Fast
'ssff',
'ss', # Slow Slow
'ssss',
'tt', # Typical Typical
'tttt',
'leak',
'ttcorreln', # Typical Typical + correlated + n?
'ttcorrelp', # Typical Typical + correlated + n?
'b',
'base', # Should this be `base_b`?
'wafer',
#'nonfet',
#'discrete',
#'subvtmm',
'mm',
'hvt', # High Voltage?
'lvt', # Low Voltage?
'h',
'native',
'subcircuit',
'rf',
'ccsnoise',
'pwr',
'powerlv',
'powerhv',
'output',
'wo', # "worst-case one" and corresponds to "fs"
'wz', # "worst-case zero" and corresponds to "sf"
'wp', # "worst-case power" and corresponds to "ff"
'ws', # "worst-case speed" and corresponds to "ss"
]
translations = {
'wo': 'fs',
'wz': 'sf',
'wp': 'ff',
'ws': 'ss',
'ffxx': 'f',
'ttxx': 't',
'ssxx': 's',
'ffff': 'ff',
'ssss': 'ss',
'ffss': 'fs',
'ssff': 'sf',
's8p': ''
}
top_extracted = False
#print(oldcorners)
for corner in oldcorners:
v = VOLTS_RE.match(corner)
if v:
top_extracted = True
coreconversion = v.group('core')
if coreconversion[0] == 'p':
coreconversion[0] = '+'
coreconversion = coreconversion.translate(invtranstab)
coreconversion = float(coreconversion)
coreconversion = f'{coreconversion:.2f}'
newcorners.append(coreconversion.translate(transtab) + 'V')
if v.group('islv') == 'lv':
newcorners.append('lv')
prev = 'volt'
prevc = corner
continue
v = TEMP_RE.match(corner)
if v:
top_extracted = True
val = str(int(v.group('value')))
newcorners.append((v.group('sign') + val + 'C').translate(transtab).replace('c', 'C'))
prev = 'temp'
prevc = corner
continue
if corner in ['x', 'lv', 'hv', 'lowhv', 'nointpwr']:
top_extracted = True
assert prev in ['volt', 'temp'], (prev, prevc, corner, old_path)
newcorners.append(corner)
prev = 'volt/temp spec'
prevc = corner
continue
if corner in types:
fincor = corner
if corner in translations:
fincor = translations[corner]
if fincor:
newcorners.append(fincor)
prev = 'additional'
prevc = corner
continue
# print(f'Unknown corner: {corner} {old_path}')
if ignoredcorners is not None:
ignoredcorners.add(corner)
prev = 'ignored'
prevc = corner
if not top_extracted:
newcorners.append(corner)
return '_'.join(newcorners)
'''
def convert_pinname(old_pinname, new_modname=None):
"""
>>> convert_pinname('CIN')
'CIN'
>>> convert_pinname('COUT')
'COUT'
>>> convert_pinname('AN')
'A_N'
>>> convert_pinname('A_N')
'A_N'
>>> convert_pinname('gn')
'GN'
>>> convert_pinname('Gp')
'GP'
>>> convert_pinname('neta')
'NETA'
>>> convert_pinname('netb')
'NETB'
>>> convert_pinname('QN')
'Q_N'
>>> convert_pinname('vnb')
'VNB'
>>> convert_pinname('vpb')
'VPB'
>>> convert_pinname("vpp_li_out")
'VPP_LI_OUT'
>>> convert_pinname("vpp_li_in")
'VPP_LI_IN'
>>> convert_pinname("vpp_li_in")
'VPP_LI_IN'
>>> convert_pinname('RESETB')
'RESET_B'
>>> convert_pinname('SETB')
'SET_B'
>>> convert_pinname('TEB')
'TE_B'
>>> convert_pinname('sleep')
'SLEEP'
>>> convert_pinname('sleepb')
'SLEEP_B'
>>> convert_pinname('SLEEPB')
'SLEEP_B'
>>> convert_pinname('GATEN')
'GATE_N'
>>> convert_pinname('dnw_vpwr')
'DNW_VPWR'
>>> convert_pinname('dnw_vgnd')
'DNW_VGND'
>>> convert_pinname('kapwr')
'KAPWR'
>>> convert_pinname('clkneg')
'CLKNEG'
>>> convert_pinname('random string')
'random string'
>>> convert_pinname('vpp_5x4_gate')
'VPP_5X4_GATE'
>>> convert_pinname('vpwr1')
'VPWR1'
>>> convert_pinname('D<10>')
'D[10]'
>>> convert_pinname('D[10]')
'D[10]'
>>> convert_pinname('D\\[10\\]')
'D[10]'
>>> convert_pinname('Substrate')
'SUBSTRATE'
>>> convert_pinname('Bulk')
'BULK'
>>> convert_pinname('Source')
'SOURCE'
>>> convert_pinname('Gate')
'GATE'
>>> convert_pinname('Drain')
'DRAIN'
>>> convert_pinname('vdd')
'VDD'
>>> convert_pinname('gnd')
'GND'
>>> convert_pinname('vdd', 'nand2_dec')
'VDD'
>>> convert_pinname('gnd', 'openram_nand4_dec')
'GND'
>>> convert_pinname('en')
'EN'
>>> convert_pinname('EN')
'EN'
"""
s = old_pinname
u = old_pinname.upper()
if ' ' in s:
return s
if '<' in s:
assert '>' in s, s
u = u.replace('<', '[')
u = u.replace('>', ']')
if '\\[' in s:
assert '\\]' in s, s
u = u.replace('\\[', '[')
u = u.replace('\\]', ']')
#if 'nand' in new_modname and 'dec' in new_modname:
# return s
if new_modname and 'dff' in new_modname:
if u == 'S':
return 'SET'
elif u == 'SB':
return 'SET_ASYNC'
elif u == 'R':
return 'RESET'
elif u == 'CK':
return 'CLK_N'
elif u == 'DE':
return 'DATA_EN'
elif u == 'CP':
return 'CLK'
elif u in ('Q', 'D', 'NOTIFIER', 'VGND', 'VPWR', 'SLEEP', 'SET', 'RESET', 'CLK_N', 'CLK', 'KAPWR', 'VDD', 'GND'):
return u
elif u == 'SLEEPB':
return 'SLEEP_B'
elif 'NOTIF' in u or u in ('NTFY', 'NFR', 'NO'):
return 'NOTIFIER'
elif u == 'ON':
return 'Q_N'
elif u in ('VDD', 'GND'):
return u
else:
assert False, (u, new_modname)
if new_modname and 'dlatch' in new_modname:
if u == 'G':
if new_modname.startswith('udp_dlatch$N') or new_modname.startswith('udp_dlatch$lN') or new_modname.startswith('udp_dlatch$uN'):
return 'GATE_N'
elif new_modname.startswith('udp_dlatch$P') or new_modname.startswith('udp_dlatch$lP') or new_modname.startswith('udp_dlatch$uP'):
return 'GATE'
else:
assert False, (u, new_modname)
elif u == 'R':
return 'RESET'
elif u in ('Q', 'D', 'NOTIFIER', 'VGND', 'VPWR', 'SLEEP', 'SET', 'RESET', 'KAPWR'):
return u
elif u == 'NOTI_REG':
return 'NOTIFIER_REG'
elif u == 'S':
return 'SET'
elif u == 'SB':
return 'SET_ASYNC'
elif u == 'SLEEPB':
return 'SLEEP_B'
elif 'NOTIF' in u or u == 'NTFY':
return 'NOTIFIER'
else:
assert False, (u, new_modname)
if re.search('(^|_)v([a-z_0-9]+)(_|$)', s):
return s.upper()
if 'SET' in u:
return u.replace('SETB', 'SET_B')
if u.startswith('TE'):
return u.replace('TEB', 'TE_B')
if 'SLEEP' in u:
return u.replace('SLEEPB', 'SLEEP_B')
if 'DB' in u:
return 'D_B'
if 'clk' in s:
return u
if 'pwr' in s:
return u
if 'gn' == s:
return u
if 'EN' == u:
return u
# Extract _N from things like GATEN
# QN -> Q_N
if re.match('^([A-Z]*[^I_])[N]$', u):
return u[:-1]+'_'+u[-1]
return u
def extract_version_and_lib_from_path(original_path):
"""
>>> extract_version_and_lib_from_path('/ssd/gob/foss-eda-tools/skywater-src-nda/s8/V1.0.0/VirtuosoOA/libs/tech/bkeep_p/verilog/verilog.v')
('s8', 'sky130_fd_pr', 'V1.0.0')
>>> extract_version_and_lib_from_path('/ssd/gob/foss-eda-tools/skywater-src-nda-openram/s8sram/V0.0.0/cells/sram_cell_1rw_1r/cell_1rw_1r.gds')
('s8sram', 'sky130_fd_bd_sram', 'V0.0.0')
"""
old_lib = lib_extract_from_path(original_path)
if not old_lib or original_path.find('vcells') > 0:
old_lib = None
new_lib = 'sky130_fd_pr'
else:
new_lib = convert_libname(old_lib)
assert new_lib, (old_lib, new_lib, original_path)
ver = version_extract_from_path(original_path)
if ver is not None:
ver = "V" + ".".join([str(v) for v in ver])
else:
ver = "V0.0.9"
assert ver is not None, ver
return (old_lib, new_lib, ver)
def convert_version(v):
"""
>>> convert_version('V0.0.0')
'v0.0.0'
>>> convert_version('V1.2.3')
'v0.12.3'
"""
assert v.startswith('V'), v
bits = v[1:].split('.')
assert len(bits) == 3, (bits, v)
ver = [int(x) for x in bits]
new_ver = [0, ver[0]*10+ver[1], ver[2]]
return "v{}.{}.{}".format(*new_ver)
skip_csv = csv.reader(open('filter.csv', 'r', newline=''))
skip_csv_header = tuple(next(skip_csv))
assert skip_csv_header == ('library', 'cellname'), skip_csv_header
SKIP_CELLS = [(r[0], r[1]) for r in skip_csv if r and len(r) > 1 and r[1]]
assert SKIP_CELLS, SKIP_CELLS
CELLS_WITH_NO_PINS = [
# WARNING: No pins on sky130_fd_pr__npn_1x1
# WARNING: No pins on sky130_fd_pr__npn_1x2
# WARNING: No pins on sky130_fd_pr__npn_1x4
# WARNING: No pins on sky130_fd_pr__npn_1x8
# WARNING: No pins on sky130_fd_pr__npn_2x2
# WARNING: No pins on sky130_fd_pr__npn_2x4
# WARNING: No pins on sky130_fd_pr__npn_2x8
# WARNING: No pins on sky130_fd_pr__npn_5x5
"__npn_",
# WARNING: No pins on sky130_fd_pr__pshort_w5p0_l0p15_m4_mc
"__pshort_w5p0_l0p15_m4_mc",
# WARNING: No pins on sky130_fd_pr__rf_pshort_w5p0_l0p15_m4_mc
"__rf_pshort_w5p0_l0p15_m4_mc",
# WARNING: No pins on sky130_fd_pr__rf_npn_1x1
# WARNING: No pins on sky130_fd_pr__rf_npn_1x2
# WARNING: No pins on sky130_fd_pr__rf_npn_1x4
# WARNING: No pins on sky130_fd_pr__rf_npn_1x8
# WARNING: No pins on sky130_fd_pr__rf_npn_2x2
# WARNING: No pins on sky130_fd_pr__rf_npn_2x4
# WARNING: No pins on sky130_fd_pr__rf_npn_2x8
# WARNING: No pins on sky130_fd_pr__rf_npn_5x5
"__rf_npn_",
# WARNING: No pins on sky130_fd_pr__pnp
"__pnp",
# WARNING: No pins on sky130_fd_pr__rf_n20nativevhviso1_noptap
# WARNING: No pins on sky130_fd_pr__rf_n20nativevhviso1_withptap
"__rf_n20nativevhviso1_",
# WARNING: No pins on sky130_fd_pr__rf_n20vhv1_aup
"__rf_n20vhv1_aup",
# WARNING: No pins on sky130_fd_pr__rf_n20vhv1_esd_hbm_21v_w60
# WARNING: No pins on sky130_fd_pr__rf_n20vhv1_esd_hbm_32v_w60
# WARNING: No pins on sky130_fd_pr__rf_n20vhv1_esd_iec_21v_w60
# WARNING: No pins on sky130_fd_pr__rf_n20vhv1_esd_iec_32v_w60
"__rf_n20vhv1_esd_",
# WARNING: No pins on sky130_fd_pr__rf_test_coil1
# WARNING: No pins on sky130_fd_pr__rf_test_coil2
# WARNING: No pins on sky130_fd_pr__rf_test_coil3
"__rf_test_coil",
# WARNING: No pins on sky130_fd_pr__xcmvpp1_subcell
# WARNING: No pins on sky130_fd_pr__xcmvpp2_subcell
"__xcmvpp",
# WARNING: No pins on sky130_fd_pr__rf_n20vhv1_withptap
# WARNING: No pins on sky130_fd_pr__rf_n20vhviso1_noptap
# WARNING: No pins on sky130_fd_pr__rf_n20vhviso1_withptap
"__rf_n20vhv",
# WARNING: No pins on sky130_fd_pr__rf_p20vhv1_withptap
"__rf_p20vhv",
# WARNING: No pins on sky130_fd_pr__rf_20v_stress_proxy
"__rf_20v_stress_proxy",
# WARNING: No pins on sky130_fd_pr__rf_pfdrc_proxy
"__rf_pfdrc_proxy",
# WARNING: No pins on sky130_fd_pr__rf_rf_fet_drc_proxy
"__rf_rf_fet_drc_proxy",
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_2x1
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_2x2
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_3x1
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_3x2
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_4x1
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_5x1
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_5x1_met5pullin
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_5x2
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_5x2_met5pullin
# WARNING: No pins on sky130_fd_pr__rf2_xcmvpp_hd5_5x2_testcase
"__rf2_xcmvpp_hd5_",
# WARNING: No pins on sky130_fd_sc_hd__patterndensity
"__patterndensity",
# WARNING: No pins on sky130_fd_sc_hd__proxy
# WARNING: No pins on sky130_fd_sc_hd__proxy_mx
# WARNING: No pins on sky130_fd_sc_hd__proxy_my
# WARNING: No pins on sky130_fd_sc_hd__proxy_r180
# WARNING: No pins on sky130_fd_sc_hd__proxy_r270
# WARNING: No pins on sky130_fd_sc_hd__proxy_r90
"__proxy",
# WARNING: No pins on sky130_fd_sc_hd__unit_tile
"__unit_tile",
]
def skip_copy_file(ext, lib, cell):
if lib == 'sky130_fd_sc_ls':
if cell.startswith('lpflow_'):
return True
if 'icecap' in cell:
return True
if 'raphael' in cell:
return True
return (lib, cell) in SKIP_CELLS
def get_final_path(final_dir, lib, ver, cellname, ext, filename=None):
"""
>>> os.path.abspath = lambda x: x
>>> get_final_path('final_dir', 'sky130_fd_pr', 'V0.0.1', 'sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p15', ext=".v")
'final_dir/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/cells/rf_nfet_01v8_lvt/sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p15.v'
>>> get_final_path('final_dir', 'sky130_fd_pr', 'V0.0.1', convert_cell_fullname('dnwdiode_pw'), ext=".v")
'final_dir/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/models/parasitics/sky130_fd_pr__model__parasitic__diode_pw2dn.v'
>>> get_final_path('final_dir', 'sky130_fd_pr', 'V0.0.1', convert_cell_fullname('xcmvpp_moscap'), ext=".spice")
'final_dir/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/models/capacitors/sky130_fd_pr__model__cap_vpp_only_mos.spice'
>>> get_final_path('final_dir', 'sky130_fd_pr', 'V0.0.1', convert_cell_fullname('nor2_p'), ext=".spice")
'final_dir/skywater-pdk/libraries/sky130_fd_pr/v0.0.1/models/typical/sky130_fd_pr__model__typical__nor2.spice'
"""
new_ver = convert_version(ver)
cell_dirname = get_cell_directory(final_dir, lib, new_ver, cellname)
if not filename:
fcellname = cellname.replace('$', '_')
filename = fcellname+ext
if 'udp' in filename:
filename = filename.lower()
final_path = os.path.join(cell_dirname, filename)
if '__' in cellname:
skip_lib, skip_cell = cellname.split('__', 1)
skip_cell_all = strip_strength(skip_cell)
if skip_copy_file(ext, skip_lib, skip_cell_all):
print(f"SKIPPING (all versions): {final_path}!")
return None
if skip_copy_file(ext, skip_lib, skip_cell):
print(f"SKIPPING (version: {skip_cell}): {final_path}!")
return None
return os.path.abspath(final_path)
def update_file_from(src_path, final_path):
assert os.path.exists(src_path), (src_path, final_path)
with open(src_path, 'rb') as f:
new_contents = f.read()
if not os.path.exists(final_path):
try:
print(f"Copying to {final_path}")
tmpfile = final_path+'.'+str(os.getpid())
try:
copyfile(src_path, tmpfile)
os.link(tmpfile, final_path)
with open(final_path+'.src', 'w') as f:
f.write(src_path)
f.write('\n')
return True
finally:
if os.path.exists(tmpfile):
os.unlink(tmpfile)
except Exception as e:
print("WARNING: update_file_from error:", e)
else:
print(f"Checking {final_path}")
try:
with open(final_path, 'rb') as f:
old_contents = f.read()
return new_contents == old_contents
except Exception as e:
print("WARNING: update_file_from error:", e)
return False
def copy_file_to_output(src_path, final_dir, lib, ver, cellname, okay_exists=False, filename=None):
assert os.path.exists(src_path), src_path
assert '/' not in cellname, cell
assert '__' in cellname or cellname in ('liberty','techlef','common','models'), cellname
ext = '.'+os.path.basename(src_path).split('.', 1)[-1]
final_path = get_final_path(final_dir, lib, ver, cellname, ext, filename)
if not final_path:
return None
# Make the directory we are going to put the file into.
os.makedirs(os.path.dirname(final_path), exist_ok=True)
# Check the files doesn't already exist
for i in range(0, 3):
if i != 0:
print(f"WARNING: Attempt {i} on writing {final_path}.")
if os.path.exists(final_path):
print(f"WARNING: {final_path} already exists!")
if update_file_from(src_path, final_path):
break
for i in range(0, 3):
if update_file_from(src_path, final_path):
return final_path
time.sleep(1)
osrc_path = ''
try:
with open(final_path+'.src', 'r') as f:
osrc_path = f.read().strip()
except IOError as e:
pass
print(f"WARNING: New file found at {final_path}.new")
copyfile(src_path, final_path+'.new')
output = subprocess.getoutput('diff --color=always -u %s %s.new' % (final_path, final_path))
assert not os.path.exists(final_path), """Existing file found with different content!
-------------------------------------
%s
-------------------------------------
Existing Source: %s
Existing file: %s
New Source: %s
New file: %s.new
""" % (output, osrc_path, final_path, src_path, final_path)
MAPPING_OLD2NEW_CELLNAME = {}
MAPPING_CELLNAME2DIR = {}
with open('names2files-b.csv', newline='') as f:
c = csv.DictReader(f)
for r in c:
MAPPING_OLD2NEW_CELLNAME[r['name'].lower()] = r['file name']
MAPPING_CELLNAME2DIR[r['file name']] = r['dir name']
def convert_cell_fullname(old_fullname, new_libname=None):
"""
>>> convert_cell_fullname("scs8hd_xor2_1")
'sky130_fd_sc_hd__xor2_1'
>>> convert_cell_fullname("scs8hd_xor2_2")
'sky130_fd_sc_hd__xor2_2'
>>> convert_cell_fullname("scs8hd_xor2_4")
'sky130_fd_sc_hd__xor2_4'
>>> convert_cell_fullname("scs8hd_xor3_1")
'sky130_fd_sc_hd__xor3_1'
>>> convert_cell_fullname("scs8ls_a2111o_1")
'sky130_fd_sc_ls__a2111o_1'
>>> convert_cell_fullname("scs8ls_a2111o_2")
'sky130_fd_sc_ls__a2111o_2'
>>> convert_cell_fullname("scs8ls_a2111o_4")
'sky130_fd_sc_ls__a2111o_4'
>>> convert_cell_fullname("scs8ls_a2111oi_1")
'sky130_fd_sc_ls__a2111oi_1'
>>> convert_cell_fullname("rf2_xcmvppx4_2xnhvnative10x4")
'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhv'
>>> convert_cell_fullname('scs8hdll_inv_12')
'sky130_fd_sc_hdll__inv_12'
SRAM cells
>>> convert_cell_fullname('s8Dp_blkinv_opt1')
'sky130_fd_bd_sram__sram_dp_blkinv_opt1'
>>> convert_cell_fullname('s8sram16x16_colend_p_cent')
'sky130_fd_bd_sram__sram_sp_colend_p_cent'
>>> convert_cell_fullname('s8sram_colend_cent')
'sky130_fd_bd_sram__sram_sp_colend_cent'
>>> convert_cell_fullname('sram_cell_1rw_1r')
'sky130_fd_bd_sram__openram_cell_1rw_1r'
>>> convert_cell_fullname('sram_dff')
'sky130_fd_bd_sram__openram_sram_dff'
>>> convert_cell_fullname('libcell_scs8hvl')
'sky130_fd_sc_hvl__libcell'
>>> convert_cell_fullname('libcell_scs8ls')
'sky130_fd_sc_ls__libcell'
>>> convert_cell_fullname('latchupcell_scs8ls')
'sky130_fd_sc_ls__latchupcell'
>>> convert_cell_fullname('scs8hdll_libcell_tap')
'sky130_fd_sc_hdll__tap'
>>> convert_cell_fullname('scs8hdll_libcell_muxb')
'sky130_fd_sc_hdll__muxb'
# Manual conversion
>>> convert_cell_fullname('xcmvpp_hd5_atlas_fingercap_l5')
'sky130_fd_pr__cap_vpp_02p7x06p1_m1m2m3m4_shieldl1_fingercap'
>>> convert_cell_fullname("nvhv")
'sky130_fd_pr__nfet_g5v0d16v0'
>>> convert_cell_fullname("pvhv")
'sky130_fd_pr__pfet_g5v0d16v0'
>>> convert_cell_fullname("s8rf2_xcmvpp11p5x11p7_lim5shield")
'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5'
>>> convert_cell_fullname("xcmvpp4p4x4p6_m3_lim5shield_top")
'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4_top'
>>> convert_cell_fullname("condiode")
'sky130_fd_pr__model__diode_connection'
>>> convert_cell_fullname("xcnwvc")
'sky130_fd_pr__cap_var_lvt'
>>> convert_cell_fullname("xcnwvc2")
'sky130_fd_pr__cap_var_hvt'
>>> convert_cell_fullname('s8blref_xind4_01')
'sky130_fd_pr__ind_01_04'
>>> convert_cell_fullname("L1M1_PR_CDNS_525975574200")
'sky130_fd_pr__l1m1_pr__example_525975574200'
>>> convert_cell_fullname('latchupcell', 'sky130_fd_sc_ms')
'sky130_fd_sc_ms__latchupcell'
>>> convert_cell_fullname('hrpoly_0p35_l1m1con_175323180')
'sky130_fd_pr__res_high_pol1m1_0p35__example1'
>>> convert_cell_fullname('hrpoly_0p35_175320108')
'sky130_fd_pr__res_high_po_0p35__example1'
>>> convert_cell_fullname('hrpoly_0p35$$175320108')
'sky130_fd_pr__res_high_po_0p35__example1'
>>> convert_cell_fullname('linear')
'sky130_fd_pr__model__linear'
>>> convert_cell_fullname('linear_top')
'sky130_fd_pr__model__linear'
>>> convert_cell_fullname('pnp_top')
'sky130_fd_pr__model__pnp'
>>> convert_cell_fullname('r+c_top')
'sky130_fd_pr__model__r+c'
>>> convert_cell_fullname('xcmimc_top')
'sky130_fd_pr__model__cap_mim'
>>> convert_cell_fullname('xcmvpp_moscap_top')
'sky130_fd_pr__model__cap_vpp_only_mos'
>>> convert_cell_fullname('xcmvpp_ponly_top')
'sky130_fd_pr__model__cap_vpp_only_p'
>>> convert_cell_fullname('xinductor_top')
'sky130_fd_pr__model__inductors'
>>> convert_cell_fullname('dnwdiode_pw_top')
'sky130_fd_pr__model__parasitic__diodes_pw2dn'
>>> convert_cell_fullname('dnwdiode_pw')
'sky130_fd_pr__model__parasitic__diode_pw2dn'
>>> convert_cell_fullname('dnwdiode_pw_no_rs')
'sky130_fd_pr__model__parasitic__diode_pw2dn_noresistor'
"""
ext_libname, ext_cellname = lib_extract_from_name(old_fullname)
assert ext_cellname is not None, (ext_cellname, old_fullname)
if 'scs8' not in old_fullname and (new_libname and '_sc_' not in new_libname):
decoder_newname = decoder_convert_cellname(ext_cellname)
if decoder_newname:
return decoder_newname
if ext_libname is not None:
xxx_libname = convert_libname(ext_libname)
if new_libname is not None:
if "_pr_rf" in xxx_libname:
assert "fd_pr" in new_libname
else:
assert xxx_libname == new_libname, (xxx_libname, new_libname, old_fullname, ext_libname)
new_libname = xxx_libname
else:
new_libname = xxx_libname
if not new_libname:
new_libname = 'sky130_fd_pr'
new_cellname = convert_cellname(ext_cellname, new_libname)
new_fullname = new_libname + '__' + new_cellname
return new_fullname
def main(ext, filemain, args, infile=None):
if infile is None:
infile = args.input
assert infile is not None, args
if not os.path.isfile(infile):
infile = pathlib.Path(infile)
if ext != 'md5sum':
g = '*.'+ext
else:
g = 'md5sum'
all_input_files = sorted(infile.rglob(g))
assert all_input_files, "No files for %s found under %s" % (g, infile)
for f in all_input_files:
f = str(f)
assert os.path.exists(f), f
assert os.path.isfile(f), f
main(ext, filemain, args, f)
else:
infile = str(infile)
# FIXME: Hacks
if ext == 'gds':
if 'scs8' in infile and 'oa' not in infile:
print("Skipping", infile)
return 0
if 'vcells_drc' in infile:
print("Skipping", infile)
return 0
if ext == 'lib':
if 'cds.lib' in infile:
print("Skipping", infile)
return 0
if 'pvtech.lib' in infile:
print("Skipping", infile)
return 0
path = os.path.abspath(infile)
if 'src-nda/' not in path:
if path[0] == '/':
after = path[1:]
else:
after = path
else:
#assert 'src-nda/' in path, path
_, after = path.split('src-nda/')
tempdir = os.path.join(args.temp, ext, after)
print()
print()
print("Processing", path, "in", tempdir)
print('-'*75)
os.makedirs(tempdir)
try:
filemain(path, tempdir, str(args.output), args)
except Exception as e:
sys.stdout.flush()
sys.stderr.flush()
traceback.print_exc(file=sys.stdout)
# Write error to stdout
sys.stdout.write('\n')
sys.stdout.write('\n')
sys.stdout.write('Error while processing: ')
sys.stdout.write(path)
sys.stdout.write('\n')
sys.stdout.flush()
# Write error to stderr too
sys.stderr.write('\n')
sys.stderr.write('\n')
sys.stderr.write('Error while processing: ')
sys.stderr.write(path)
sys.stderr.write('\n')
sys.stderr.flush()
raise
print('-'*75)
__dir__ = os.path.abspath(os.path.dirname(__file__))
descriptions_file = os.path.join(__dir__, 'descriptions.tsv')
assert os.path.exists(descriptions_file), descriptions_file
DESCRIPTIONS = {}
EQUATIONS = {}
with open(descriptions_file, newline='') as f:
reader = csv.reader(f, delimiter='\t', quoting=csv.QUOTE_NONE)
header = list(next(reader))
assert header[0] == 'module name', header
for row in reader:
if not row[-2]:
continue
if row[-2] == '--fromfile--':
continue
DESCRIPTIONS[row[0]] = row[-2].strip()
eq = row[-1].strip()
if eq:
EQUATIONS[row[0]] = eq
if __name__ == "__main__":
import doctest
fails, _ = doctest.testmod()
if fails != 0:
sys.exit("Some test failed!")
'''
liberty_files = {}
with open('liberty-files.txt', 'r') as f:
for l in f:
# FIXME: ignore these for now...
if 'scs8hdll' in l:
continue
l = l.strip()
fn = l.rsplit('/')[-1]
if fn == "cds.lib":
continue
assert '_' in fn, (fn, l)
file_libname, file_spec = fn.split('_', 1)
old_libname, old_corner = corners_extract_from_filename_lib(fn)
assert file_libname == old_libname, (fn, file_libname, old_libname)
old_lib, new_lib, ver = extract_version_and_lib_from_path(l)
newcorner = convert_corner(file_spec)
newlibname = convert_libname(old_libname)
print()
print(fn, old_libname, old_lib)
print(old_corner)
print(newcorner)
print()
k = (newlibname, ver, newcorner)
assert k not in liberty_files, (newcorner, l, liberty_files[k])
liberty_files[k] = l
'''
sys.exit(1)
for i in copyright_header:
print('----')
pprint.pprint(copyright_header[i])
print('----')
for n, desc in DESCRIPTIONS.items():
print("| %40s" % n, "| %-40s" % EQUATIONS.get(n, ''), '|', desc)