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