| #!/usr/bin/env python3 |
| |
| import csv |
| from pathlib import Path |
| import argparse |
| from collections import defaultdict |
| import shlex |
| import re |
| from termcolor import colored |
| import sys |
| from prettytable import PrettyTable |
| import json |
| |
| sourcetodests = defaultdict(list) |
| |
| RE_SUBCKT = re.compile(r'\.subckt\s+(?P<subcktname>[^ ]+)\s?(?P<nodenames>.*)') |
| # RE_MODEL = re.compile(r'^[\.]*model\s+(?P<modelname>[^ ]+)\s+(?P<modeltype>[^ ]+)') |
| |
| RE_MODEL_END = re.compile(r'^[\s\n]*ends(?P<end>.*)(\n|$)') |
| RE_SUBCKT_END = re.compile(r'^.ends') |
| RE_GROUP = re.compile(r'[*\s]*?(?P<group>[^*\n]+?)[*\s]*(\n|$)') |
| RE_MODE_LINE = re.compile(r'(?P<modeid>\d+)\s*:\s*type=\s*(?P<modetype>.*)(\n|$)') |
| RE_INLINE_SUBCKT_MODEL = re.compile(r'^(?P<subcktname>[^ ]+)\s*\((?P<nodenames>.*)\)\s*(?P<modelname>[^ ]+)\s*(?P<parameters>.*)($|\n)') |
| |
| RE_MODEL = re.compile(r'[\.]?model') |
| |
| def order_parameters(paramname): |
| priority = { |
| 'subckt': 0, |
| 'nodes': 1, |
| 'model': 2, |
| 'type': 3, |
| 'mode': 4, |
| 'modetype': 5, |
| 'lmin': 6, |
| 'lmax': 7, |
| 'wmin': 8, |
| 'wmax': 9, |
| 'level': 10, |
| 'tnom': 11, |
| 'version': 12, |
| 'tox': 13, |
| 'toxm': 14, |
| } |
| if paramname in priority: |
| return (priority[paramname], paramname) |
| return (1000, paramname) |
| |
| |
| def pm3_to_csv(lines): |
| result = [] |
| params = {} |
| params['subckt'] = {} |
| params['model'] = {} |
| params['mode'] = {} |
| scopetype = None |
| def produce_line(): |
| finparams = {} |
| finparams.update(params['subckt']) |
| finparams.update(params['model']) |
| finparams.update(params['mode']) |
| if len(finparams) > 0: |
| result.append(finparams) |
| scopetype = None |
| for line in lines: |
| try: |
| if not line.strip(): |
| continue |
| line = line.strip() |
| elements = shlex.split(line, posix=False) |
| if line.startswith('*'): |
| continue |
| m = RE_MODE_LINE.match(line) |
| if m: |
| if scopetype == 'mode': |
| produce_line() |
| params['mode'] = {} |
| params['mode']['mode'] = m.group('modeid') |
| params['mode']['modetype'] = m.group('modetype') |
| scopetype = 'mode' |
| continue |
| ind = 0 |
| def parse_parameters(): |
| if scopetype is None: |
| return |
| ind = 0 |
| prevkey = None |
| while ind < len(elements): |
| if not elements[ind].strip(): |
| ind += 1 |
| elif elements[ind].endswith('='): |
| if scopetype is None: |
| print(colored(line, 'magenta'), file=sys.stderr) |
| ind += 2 |
| continue |
| params[scopetype][elements[ind][:-1]] = elements[ind+1] |
| prevkey = elements[ind][:-1] |
| ind += 2 |
| elif '=' in elements[ind]: |
| res = elements[ind].split('=') |
| params[scopetype][res[0]] = res[1] |
| prevkey = res[0] |
| ind += 1 |
| elif prevkey is not None: |
| params[scopetype][prevkey] += elements[ind] |
| ind += 1 |
| else: |
| print(colored(line, 'cyan'), file=sys.stderr) |
| raise Exception(line) |
| if elements[0] == '.subckt': |
| produce_line() |
| params['subckt'] = {} |
| params['model'] = {} |
| params['mode'] = {} |
| params['subckt']['subckt'] = elements[1] |
| nodelist = [] |
| ind = 2 |
| for element in elements[2:]: |
| if '=' in element: |
| break |
| element = element.replace('(', '') |
| element = element.replace(')', '') |
| nodelist.append(element) |
| ind += 1 |
| params['subckt']['nodes'] = ','.join(nodelist) |
| scopetype = 'subckt' |
| elements = elements[ind:] |
| parse_parameters() |
| elif RE_MODEL.match(elements[0]): |
| if scopetype != 'subckt': |
| produce_line() |
| params['model'] = {} |
| params['mode'] = {} |
| params['model']['model'] = elements[1] |
| params['model']['type'] = elements[2] |
| scopetype = 'model' |
| if len(elements) >=4 and elements[3] == '{': |
| continue |
| elements = elements[3:] |
| parse_parameters() |
| elif elements[0].startswith('+'): |
| elements[0] = elements[0][1:] |
| parse_parameters() |
| elif elements[0] == 'parameters': |
| elements = elements[1:] |
| parse_parameters() |
| else: |
| m = RE_INLINE_SUBCKT_MODEL.match(line) |
| if m: |
| if scopetype is not None: |
| produce_line() |
| params['subckt'] = {} |
| params['model'] = {} |
| params['mode'] = {} |
| scopetype = 'subckt' |
| params['subckt']['subckt'] = m.group('subcktname') |
| params['subckt']['nodes'] = ','.join(m.group('nodenames').split(' ')) |
| params['model']['model'] = m.group('modelname') |
| params['model']['type'] = params['subckt']['subckt'] |
| elements = m.group('parameters').split() |
| parse_parameters() |
| else: |
| print(colored(line, 'yellow'), file=sys.stderr) |
| except: |
| print(colored(line, 'red'), file=sys.stderr) |
| raise |
| produce_line() |
| parameters = set() |
| for entry in result: |
| parameters.update(entry.keys()) |
| |
| parameters = sorted(parameters, key=order_parameters) |
| |
| csvcontent = [parameters] |
| |
| for entry in result: |
| row = [] |
| for param in parameters: |
| if param in entry: |
| row.append(entry[param]) |
| else: |
| row.append('N/A') |
| csvcontent.append(row) |
| |
| return csvcontent |
| |
| if __name__ == '__main__': |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| "input_dir", |
| help="The path to the directory containing skywater-pdk", |
| type=Path) |
| parser.add_argument( |
| "--sourcetodests", |
| help="Mapping from source files to destination files", |
| type=Path) |
| parser.add_argument( |
| "--dry-run", |
| help="Do not perform actions on filesystem, only print", |
| action="store_true") |
| |
| args = parser.parse_args() |
| |
| files = sorted(args.input_dir.rglob('*.pm3')) |
| |
| allfilesnum = len(files) |
| |
| parameters = [] |
| # file model parameter value |
| filesdata = defaultdict(lambda: defaultdict(dict)) |
| |
| for num, filename in enumerate(files): |
| with open(filename, 'r') as data: |
| print(f'[{num:05d}/{allfilesnum:05d}]: {filename}') |
| print(f'[{num:05d}/{allfilesnum:05d}]: {filename}', file=sys.stderr) |
| # parameters across many entries |
| lines = [] |
| for line in data.readlines(): |
| lines.append(line) |
| |
| result = pm3_to_csv(lines) |
| if len(result) > 1: |
| with open(filename.with_suffix('.csv'), 'w', newline='') as csvfile: |
| writer = csv.writer(csvfile, delimiter=';') |
| writer.writerows(result) |
| sourcetodests[str(filename)].append(str(Path(filename).with_suffix('.csv'))) |
| |
| with open(filename.with_suffix('.table'), 'w') as tablefile: |
| t = PrettyTable(result[0]) |
| t.align = 'r' |
| t.border = False |
| for line in result[1:]: |
| t.add_row(line) |
| tablefile.write(str(t)) |
| sourcetodests[str(filename)].append(str(Path(filename).with_suffix('.table'))) |
| with open(args.sourcetodests, 'w') as srctodst: |
| json.dump(sourcetodests, srctodst, indent=2) |