blob: 2ff9521ab50fac979715b1ea0410f0f0aa2323ef [file] [log] [blame]
#!/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)