| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2020 The SkyWater PDK Authors. |
| # |
| # Use of this source code is governed by the Apache 2.0 |
| # license that can be found in the LICENSE file or at |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import csv |
| import os |
| import pathlib |
| import textwrap |
| import sys |
| |
| from collections import OrderedDict |
| |
| from common import copyright_header |
| |
| |
| def wrap_comment(prefix, s): |
| p = '* '+prefix+' ' |
| m = '* '+(' '*len(prefix))+' ' |
| |
| return "\n".join(textwrap.wrap( |
| s, initial_indent=p, subsequent_indent=m)) |
| |
| |
| def wrap_pininfo(s): |
| prefix = '*.PININFO ' |
| return "\n".join(textwrap.wrap( |
| s, initial_indent=prefix, subsequent_indent=prefix)) |
| |
| |
| def wrap_line(s): |
| return "\n".join(textwrap.wrap( |
| s, initial_indent='', subsequent_indent='+ ')) |
| |
| |
| |
| def attribs(row): |
| o = [] |
| for k, v in row.items(): |
| if not v: |
| continue |
| if isinstance(k, int): |
| o.append(v) |
| else: |
| o.append(f'{k}={v}') |
| return ' '.join(o) |
| |
| |
| def remap(rows): |
| headers = next(rows) |
| for i, h in enumerate(headers): |
| if not h: |
| headers[i] = i |
| o = [] |
| for r in rows: |
| o.append(OrderedDict(zip(headers, r))) |
| return o |
| |
| |
| def convert_tsv2cdl(path): |
| assert path.endswith('.netlist.tsv') |
| |
| dirname = os.path.dirname(path) |
| filename = os.path.basename(path) |
| basename = filename.split('.', 1)[0] |
| outpath = os.path.join(dirname, basename+'.cdl') |
| |
| |
| f_in = open(path, newline='') |
| f_out = open(outpath, 'w') |
| f_out.write(copyright_header['*']) |
| |
| tsv_in = csv.reader(f_in, delimiter='\t') |
| |
| row0 = list(next(tsv_in)) |
| rows = remap(tsv_in) |
| |
| subckt_name = row0[0] |
| subckt_pins = row0[1] |
| |
| extra = [] |
| while rows and rows[0]['Name'] == '': |
| extra = [f'{a}={b}' for a, b in rows.pop(0).items() if a] |
| |
| f_out.write(f'.SUBCKT {subckt_name} ') |
| f_out.write(" ".join(a.split(':')[0] for a in subckt_pins.split())) |
| if extra: |
| f_out.write(' ') |
| f_out.write(' '.join(extra)) |
| f_out.write('\n') |
| f_out.write(wrap_pininfo(f"{subckt_pins}")) |
| f_out.write('\n') |
| |
| for row in rows: |
| formula = row.pop('Formula') |
| if row['Name'] == 'Note': |
| f_out.write(wrap_comment('Notes:', formula)) |
| elif formula == 'MOSFET': |
| rowname = row.pop('Name') |
| nd = row.pop('nd') |
| ng = row.pop('ng') |
| ns = row.pop('ns') |
| nb = row.pop('nb') |
| mname = row.pop('mname') |
| f_out.write(wrap_line( |
| f"{rowname} {nd} {ng} {ns} {nb} {mname} "+attribs(row))) |
| elif formula in ('DIODE', 'RESISTOR'): |
| f_out.write(wrap_line( |
| f"{row.pop('Name')} " + attribs(row))) |
| elif row['Name'][0].upper() in ('R', 'X'): |
| f_out.write(wrap_line( |
| f"{row['Name']} {formula}")) |
| else: |
| assert False, (row, path) |
| f_out.write('\n') |
| |
| f_out.write(f'.ENDS {subckt_name}\n') |
| print(f"Created {outpath}") |
| |
| |
| |
| def main(args): |
| for a in args: |
| a = os.path.abspath(a) |
| if os.path.isdir(a): |
| return main(sorted(pathlib.Path(a).rglob('*.netlist.tsv'))) |
| elif os.path.isfile(a): |
| convert_tsv2cdl(a) |
| else: |
| raise IOError(f"Unknown type: {a}") |
| return 0 |
| |
| |
| if __name__ == "__main__": |
| import doctest |
| fails, _ = doctest.testmod() |
| if fails != 0: |
| exit(1) |
| sys.exit(main(sys.argv[1:])) |